В чем разница между интерфейсами и дженериками в Go?

Ответ

Интерфейсы и дженерики — это два разных механизма для написания полиморфного кода в Go. Они решают разные задачи и имеют разные принципы работы.

Интерфейсы (Полиморфизм поведения)

  • Что это? Интерфейс определяет контракт поведения — набор методов, которые тип должен реализовать. Он отвечает на вопрос: "Что этот тип умеет делать?".
  • Проверка: Проверка соответствия интерфейсу происходит в runtime (динамическая диспетчеризация).
  • Гибкость: Позволяют создавать гетерогенные (разнородные) коллекции. Например, слайс []io.Writer может содержать и *os.File, и *bytes.Buffer.
// Интерфейс определяет поведение "умеет говорить"
type Speaker interface {
    Speak() string
}

// Функция работает с любым типом, который удовлетворяет интерфейсу Speaker
func Greet(s Speaker) {
    fmt.Println(s.Speak())
}

Дженерики (Параметрический полиморфизм)

  • Что это? Дженерики позволяют писать функции и типы, которые работают с любым типом из заданного набора, сохраняя при этом строгую типизацию. Они отвечают на вопрос: "С какими типами данных работает этот алгоритм?".
  • Проверка: Проверка типов происходит на этапе компиляции.
  • Производительность: Код с дженериками обычно быстрее, так как компилятор генерирует специализированные версии функций для конкретных типов, избегая накладных расходов runtime.
// Дженерик-функция для поиска индекса элемента в слайсе любого сравнимого типа
func IndexOf[T comparable](slice []T, value T) int {
    for i, v := range slice {
        if v == value {
            return i
        }
    }
    return -1
}

Сравнительная таблица

ХарактеристикаИнтерфейсыДженерики
Основная идеяАбстракция над поведениемАбстракция над типами
Проверка типовRuntime (динамическая)Compile-time (статическая)
ПроизводительностьНебольшие накладные расходы в runtimeБлизко к нативному коду без полиморфизма
КоллекцииГетерогенные (разные типы)Гомогенные (один тип T в рамках вызова)

Когда что использовать?

  • Используйте интерфейсы, когда вам важна общность поведения (например, io.Reader, http.Handler).
  • Используйте дженерики, когда вы пишете алгоритмы для структур данных (слайсы, мапы, деревья) и хотите сохранить типобезопасность и производительность.