В чем ключевые отличия интерфейсов в Go от интерфейсов в классических ООП-языках (Java, C#)?

Ответ

Основное отличие интерфейсов в Go заключается в их неявной реализации, что также называют "утиной типизацией" (duck typing). В классических ООП-языках, таких как Java или C#, реализация интерфейса всегда явная.

// Определяем интерфейс
type Writer interface {
    Write(p []byte) (n int, err error)
}

// Определяем тип. Он не имеет никакой информации об интерфейсе Writer.
type ConsoleWriter struct{}

// Реализуем метод с той же сигнатурой, что и в интерфейсе.
func (cw ConsoleWriter) Write(p []byte) (int, error) {
    return fmt.Println(string(p))
}

// Переменной типа Writer можно присвоить значение типа ConsoleWriter,
// так как ConsoleWriter *неявно* удовлетворяет интерфейсу.
var w Writer = ConsoleWriter{}

Ключевые отличия:

  1. Неявная реализация (Утиная типизация): Тип удовлетворяет интерфейсу автоматически, если у него есть все методы этого интерфейса. Не нужно ключевое слово implements.

    • Следствие: Это позволяет реализовывать интерфейсы для типов из сторонних библиотек, не изменяя их исходный код.
  2. Отсутствие иерархии: В Go интерфейсы не создают иерархии наследования. Они определяют поведение (что тип умеет делать), а не родство (чем тип является).

  3. Композиция интерфейсов: Интерфейсы в Go легко встраиваются друг в друга, создавая более сложные контракты из простых.

    type ReadWriter interface {
        Reader
        Writer
    }
  4. Пустой интерфейс (any): Тип interface{} (с версии Go 1.18 — псевдоним any) может хранить значение абсолютно любого типа. Это мощный инструмент для создания гибких API, но требует проверки типа при использовании (type assertion).

В итоге, интерфейсы в Go — это более гибкий и легковесный инструмент, способствующий слабой связанности (loose coupling) компонентов системы.