Тип интерфейса
Тип интерфейса состоит из набора сигнатур методов. Переменная типа интерфейса может содержать любое значение, которое реализует эти методы.
В этом примере и Temp, и *Point реализуют интерфейс MyStringer.
type MyStringer interface {
String() string
}
type Temp int
func (t Temp) String() string {
return strconv.Itoa(int(t)) + " °C"
}
type Point struct {
x, y int
}
func (p *Point) String() string {
return fmt.Sprintf("(%d,%d)", p.x, p.y)
}
На самом деле, *Temp также реализует MyStringer, так как набор методов типа указателя *T является набором всех методов с приемником *T или T.
Когда вы вызываете метод для значения интерфейса, выполняется метод его базового типа.
var x MyStringer
x = Temp(24)
fmt.Println(x.String()) // 24 °C
x = &Point{1, 2}
fmt.Println(x.String()) // (1,2)
Структурная типизация
Тип реализует интерфейс, реализуя его методы. Никакого явного объявления не требуется.
Фактически, типы Temp, *Temp и *Point также реализуют стандартный интерфейс библиотеки fmt.Stringer. Метод String в этом интерфейсе используется для печати значений, передаваемых в качестве операнда таким функциям, как fmt.Println.
var x MyStringer
x = Temp(24)
fmt.Println(x) // 24 °C
x = &Point{1, 2}
fmt.Println(x) // (1,2)
Пустой интерфейс
Тип интерфейса, который не указывает никаких методов, называется пустым интерфейсом.
interface{}
Пустой интерфейс может содержать значения любого типа, поскольку каждый тип реализует как минимум ноль методов.
var x interface{}
x = 2.4
fmt.Println(x) // 2.4
x = &Point{1, 2}
fmt.Println(x) // (1,2)
Функция fmt.Println - главный пример. Она принимает любое количество аргументов любого типа.
func Println(a ...interface{}) (n int, err error)
Значения интерфейса
Значение интерфейса состоит из конкретного значения и динамического типа: [Value, Type]
При вызове fmt.Printf вы можете использовать %v для печати конкретного значения и %T для печати динамического типа.
var x MyStringer
fmt.Printf("%v %T\n", x, x) // <nil> <nil>
x = Temp(24)
fmt.Printf("%v %T\n", x, x) // 24 °C main.Temp
x = &Point{1, 2}
fmt.Printf("%v %T\n", x, x) // (1,2) *main.Point
x = (*Point)(nil)
fmt.Printf("%v %T\n", x, x) //
Нулевым значением типа интерфейса является nil, которое представляется как [nil, nil].
Вызов метода на нулевом интерфейсе является ошибкой во время выполнения. Однако довольно часто пишут методы, которые могут обрабатывать значение получателя [nil, Type], где Type не равен nil.
Вы можете использовать утверждения типа или переключатели типа для доступа к динамическому типу значения интерфейса.
Равенство
Два значения интерфейса равны
- если они имеют одинаковые конкретные значения и идентичные динамические типы,
- или если оба равны nil.
Значение t интерфейса типа T и значение x неинтерфейсного типа X равны, если
- конкретное значение t равно х
- и динамический тип t идентичен X.
var x MyStringer
fmt.Println(x == nil) // true
x = (*Point)(nil)
fmt.Println(x == nil) // false
Во втором операторе print конкретное значение x равно nil, но его динамический тип - *Point, а не nil.
Читайте также:
- Спецификация Go: тип интерфейса (interface type)
- Эффективный Go: интерфейсы, преобразования
- Основы Go: интерфейсы
Комментариев нет:
Отправить комментарий