пятница, 14 февраля 2020 г.

Тип, значение и равенство интерфейсов в Golang

Тип интерфейса

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

В этом примере и 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) //  *main.Point

Нулевым значением типа интерфейса является 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.


Читайте также:


Комментариев нет:

Отправить комментарий