Dev48
ЯЗЫК
  • О нас
  • Услуги
  • Индустрии
  • Технологии
  • Статьи
  • Контакты
Забронировать звонок
    Главная/Статьи/Obobschennye metody
Dev48
info@dev48.org+375 29 320-40-96WhatsApp@isky_by

© 2026 · Все права защищены.

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

Источник: Go•27 сентября 2026 г. в 08:29

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

Go 1.27 adds generic methods—a highly desired language feature.

Обобщения стали серьезным изменением для Go: они кардинально расширили круг программ, которые могут писать разработчики на 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 с каждым возможным аргументом типа.

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

Заключение

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

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

← Все статьи

О чём эта статья