Интерфейсы

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

Интерфейс — это набор сигнатур методов. Тип реализует интерфейс автоматически, если имеет все эти методы — никакого implements писать не нужно.

Неявная реализация — главная идея

В Java или C# вы пишете class Dog implements Animal. В Go такого нет. Интерфейс — это контракт «какие методы должны быть», а тип удовлетворяет ему просто фактом наличия этих методов. Это называется структурной типизацией или «утиной типизацией на этапе компиляции».

package main

import "fmt"

// интерфейс: контракт из одного метода
type Shape interface {
    Area() float64
}

type Circle struct{ R float64 }
type Square struct{ Side float64 }

// Circle и Square нигде не пишут "implements Shape" —
// они реализуют его автоматически, просто имея метод Area.
func (c Circle) Area() float64 { return 3.14159 * c.R * c.R }
func (s Square) Area() float64 { return s.Side * s.Side }

func describe(sh Shape) {
    fmt.Printf("площадь: %.2f\n", sh.Area())
}

func main() {
    describe(Circle{R: 2})
    describe(Square{Side: 3})
}

Вывод:

площадь: 12.57
площадь: 9.00

Функция describe принимает любой Shape. И Circle, и Square подходят, потому что у них есть Area(). Добавите новый тип с Area() — он тоже заработает, не меняя describe.

Пустой интерфейс

Интерфейс без методов interface{} (с Go 1.18 есть псевдоним any) удовлетворяется любым типом. Это аналог Object в Java или any в TypeScript — переменная, способная хранить что угодно.

package main

import "fmt"

func main() {
    var x any // то же, что interface{}
    x = 42
    fmt.Println(x)
    x = "строка"
    fmt.Println(x)
}

Вывод:

42
строка

Type assertion: достаём конкретный тип

Когда значение лежит в интерфейсе, его конкретный тип «спрятан». Достать его обратно помогает type assertion с проверкой ok — она безопасна и не паникует, если тип не тот.

package main

import "fmt"

func main() {
    var v any = "привет"

    if s, ok := v.(string); ok {
        fmt.Println("строка длиной", len(s))
    }
    if _, ok := v.(int); !ok {
        fmt.Println("это не int")
    }
}

Вывод:

строка длиной 12
это не int

Для разбора по нескольким типам удобен switch v := x.(type) — это type switch, ветвление по динамическому типу значения.

Итог

  • Интерфейс — набор методов; тип реализует его неявно, просто имея эти методы.
  • Пустой интерфейс any (interface{}) принимает значение любого типа.
  • Type assertion v.(T) с проверкой ok безопасно достаёт конкретный тип.
Проверьте себя
1. Как тип в Go реализует интерфейс?
AЧерез ключевое слово implements
BАвтоматически, если имеет все методы интерфейса
CЧерез наследование
DРегистрацией в реестре типов
2. Что может хранить переменная типа any (interface{})?
AТолько числа
BТолько строки
CЗначение любого типа
DТолько структуры
3. Зачем нужна форма v.(string) с проверкой ok?
AЧтобы преобразовать число в строку
BЧтобы безопасно достать конкретный тип из интерфейса без паники
CЧтобы создать новый интерфейс
DЧтобы удалить значение
Поддержать проект