Интерфейсы: определение, неявная реализация и type assertion
Содержание курса
В прошлом уроке мы дали Counter набор методов с pointer-receiver — Increment, Reset, Value. Теперь посмотрим, как именно этот набор превращается в нечто большее: тип автоматически начинает «удовлетворять» интерфейсу, не сообщая об этом явно ни компилятору, ни читателю кода.
Объявление интерфейса и неявная реализация через совпадение сигнатур
Интерфейс в Go — это просто перечень сигнатур методов. Никакой реализации, никаких полей.
type Stringer interface {
String() string
}
Вот и всё объявление. Компилятор читает его так: «любой тип, у которого есть метод String(), возвращающий string, удовлетворяет этому интерфейсу». Никакого implements Stringer, никакой регистрации — достаточно совпадения сигнатур.
Определим два типа:
type Dog struct{ Name string }
type Cat struct{ Name string }
func (d Dog) String() string { return "Dog: " + d.Name }
func (c Cat) String() string { return "Cat: " + c.Name }
Оба типа реализуют Stringer — компилятор проверяет это в момент присваивания или передачи в функцию. Если сигнатуры совпадают, всё работает; если нет — ошибка компиляции с точным указанием, какого метода не хватает.
Теперь важный нюанс, который мы уже встречали при разборе pointer-receiver. Посмотри на такой вариант:
type Counter struct{ n int }
func (c *Counter) String() string {
return fmt.Sprintf("counter=%d", c.n)
}
Здесь метод объявлен на *Counter, а не на Counter. Значит, интерфейс Stringer реализует тип *Counter — именно указатель. Значение Counter (не указатель) этому интерфейсу не удовлетворяет:
var s Stringer
s = &Counter{} // OK: *Counter реализует Stringer
s = Counter{} // ошибка компиляции: Counter не реализует Stringer
Правило простое: набор методов T включает только те методы, у которых receiver — T. Набор методов *T включает методы и с receiver T, и с receiver *T. Поэтому *T всегда реализует как минимум то, что реализует T, но не наоборот.
Переменная интерфейсного типа хранит пару: конкретное значение и конкретный тип. Её нулевое значение — nil. Попытка вызвать метод на nil-интерфейсе приводит к панике, потому что нет ни значения, ни типа, по которому можно найти метод.
