Dev48
ЯЗЫК
  • О нас
  • Услуги
  • Индустрии
  • Технологии
  • Статьи
  • Контакты
Забронировать звонок
    Главная/Статьи/Obobschennye metody
Dev48

© 2026 · All rights reserved.

Обобщенные методы

Источник: Go

Обобщенные методы

Источник: Go

В Go 1.27 добавлены обобщенные методы — функция языка, которую очень ждали.

25 сентября 2026 г.

Дженерики стали серьезным изменением для Go: они кардинально расширили круг задач, которые могут решать разработчики, добавив в язык параметры типов.

Теперь в Go можно выражать обобщенные типы и функции. Эти конструкции снизили потребность в специализированных типах данных и функциях, уменьшив многословность кода и повысив удобство использования языка в соответствующих сценариях.

В качестве иллюстрации, различные виды связанных списков теперь можно свести к единому определению типа:

Аналогично, сортировку различных видов упорядоченных данных можно выразить в виде одной функции:

Однако методы не получили подобных возможностей.

В предложении по дженерикам обосновывалось, что поскольку методы обобщенных интерфейсов сложно реализовать эффективно (об этом пойдет речь ниже), добавлять параметры типов к неинтерфейсным (или «конкретным») методам тоже не имеет смысла. С этой точки зрения методы служат в первую очередь средством реализации интерфейса.

В Go 1.27 принята иная точка зрения, и в результате в Go добавляются обобщенные методы. В этой статье мы объясним это изменение взгляда и продемонстрируем некоторые варианты использования новой возможности.

Методы для организации

Методы позволяют организовывать функциональность вокруг типов. Чтобы проиллюстрировать это, давайте вернемся к упомянутому выше связанному списку (добавив несколько удобств):

Предположим, кто-то захочет отобразить эту структуру так, чтобы она содержала значение какого-то другого типа, например строки. Метод ToString сделает следующее:

Предоставляя функцию «преобразования» f, ToString можно настраивать с помощью готовых подпрограмм.

Например, strconv.Itoa может подойти для List[int]:

Для List[[]byte] на ум приходит больше вариантов (в зависимости от сценария использования):

Обратите внимание, что параметризация List позволила обобщить исходный тип, но не целевой тип, так как он зависит от применяемого преобразования. Это может быть разумно только для одного целевого типа, но что, если их много?

В Go 1.18 для этого можно было использовать обобщенную функцию:

Но использование функции имеет тот недостаток, что MapList переносится в область видимости пакета — если множество типов данных поддерживает операции отображения, код может стать перегруженным. Кроме того, вложенные вызовы приходится писать «изнутри наружу»:

Поскольку в Go 1.18 не было поддержки обобщенных методов, нам требовалась обобщенная функция в качестве обходного пути. Теперь в Go 1.27 это исправлено:

Это компактно, выразительно и локализовано. Кроме того, цепочки вызовов более читаемы, так как их можно писать более естественно слева направо:

Предпочитаете форму «изнутри наружу»? Выражение метода может преобразовать любой метод, включая обобщенный, в его эквивалент в виде функции. Таким образом, при желании можно восстановить структуру вызова функции:

Обобщенные методы, как и другие дженерики в Go, должны быть инстациированы (явно или неявно) перед их использованием (вызовом или преобразованием в функцию).

Разделение понятий

Если рассматривать методы также как инструмент организации, то логика Go 1.18 кажется излишне ограничивающей. Хотя конкретный обобщенный метод не может помочь в реализации интерфейса (без обобщенных методов интерфейса), он все равно может быть полезен для организации кода. Другими словами, эти аспекты можно разделить.

Уточним это на примере простого интерфейса:

Здесь T.M параметризован через P. Любая инстанциация T.M приведет к сигнатуре метода, идентичной сигнатуре I.M:

И T.M[int], и I.M имеют сигнатуру func M(). Однако это не означает, что T реализует I. Реализация интерфейса — это свойство (возможно, инстанцированного) типа, а не какого-то конкретного метода. Важно отметить, что T объявляет не T.M[int], а свой обобщенный (и, следовательно, неинстанцированный) аналог T.M.

Чтобы T.M участвовал в реализации интерфейса, должен существовать подходящий обобщенный метод I.M:

Поскольку синтаксис Go 1.27 не позволяет методам интерфейса объявлять параметры типов, написать такой код невозможно.

Но почему в Go не может быть обобщенных методов интерфейса? Чтобы разобраться, потребуется небольшое отступление.

Проблема с обобщенными методами интерфейса

Значение интерфейса — это коробка, которая может содержать некоторое другое значение. Упакованное значение может быть любого типа, если этот тип реализует интерфейс. Это означает, что объявленные методы типа являются надмножеством методов, объявленных в интерфейсе.

Для иллюстрации: ниже T реализует I:

Методы можно вызывать у значений интерфейса:

Выше легко заметить, что вызов i.M будет направлен на T.M. Но в более сложных программах связь между вызовом метода интерфейсного значения и его возможными целями значительно менее очевидна. Эта связь обычно охватывает разные пакеты, что затрудняет или делает невозможным ее выведение в общем случае.

В качестве иллюстрации введем границу пакета:

Поскольку два пакета компилируются отдельно, главный пакет не будет знать, как p.F использует T{}. Чтобы гарантировать наличие любого метода, который может вызвать p.F, компилятор генерирует код для всех не-обобщенных методов типа при его объявлении (или инстанциации). Таким образом, независимо от того, что p.F делает с T{}, необходимый код будет существовать во время выполнения.

Теперь предположим, что I.M является обобщенным:

Снова главный пакет не будет знать, как p.F использует T{}, включая то, как он может инстанцировать T.M. Чтобы обрабатывать любое использование T{} внутри p.F, компилятору потребуется инстанцировать T.M с каждым возможным аргументом типа.

Это непрактично при подходe Go к инстанциации, при котором компилятор генерирует специфический код для каждого метода на основе аргументов типа. Если бы аргументы методов были «упакованы» (то есть передавались как значения своих интерфейсов ограничений), каждая инстанциация разделяла бы один и тот же код. Это позволило бы избежать слишком большого количества инстанциаций ценой накладных расходов на косвенные вызовы — даже для прямых вызовов инстанцированных методов.

Заключение

Go 1.27 представляет параметры типов для конкретных методов — эту функцию очень жгло сообщество Go, так как она позволяет писать более удобный и читаемый код. Хотя мы не можем поддержать обобщенные методы интерфейса, мы решили, что разрешение параметров типов для конкретных методов все равно того стоит.

Мы надеемся, что вам понравится использовать обобщенные методы Go и вы найдете для них полезные применения в своих проектах!

← Все статьи