вторник, 15 января 2019 г.

Основы Go: интерфейсные значения

Интерфейсное значение можно рассматривать как пару из значения и конкретного типа:

(value, type)

Интерфейсное значение содержит значение конкретного нижележащего типа.

Вызов метода интерфейсного значения выполняет метод с таким же именем у его нижележащего типа.

package main

import (
  "fmt"
  "math"
)

type I interface {
  M()
}

type T struct {
  S string
}

func (t *T) M() {
  fmt.Println(t.S)
}

type F float64

func (f F) M() {
  fmt.Println(f)
}

func main() {
  var i I

  i = &T{"Hello"}
  describe(i)
  i.M()

  i = F(math.Pi)
  describe(i)
  i.M()
}

func describe(i I) {
  fmt.Printf("(%v, %T)\n", i, i)
}

Вывод:

(&{Hello}, *main.T)
Hello
(3.141592653589793, main.F)
3.141592653589793

Интерфейсные значения с нижележащим типом nil

Если нижележащим значением интерфейсного значения является nil, то метод будет вызван с получателем nil.

В некоторых языках это привело бы к исключению нулевого указателя, но в Go обычно пишут методы, которые обрабатывают ситуацию, когда они вызваны с получателем nil (как в случае с методом M в следующем примере).

Обратите внимание, что если интерфейсное значение имеет nil в качестве нижележащего значения, то само по себе оно не nil.

package main

import "fmt"

type I interface {
  M()
}

type T struct {
  S string
}

func (t *T) M() {
  if t == nil {
    fmt.Println("")
    return
  }
  fmt.Println(t.S)
}

func main() {
  var i I

  var t *T
  i = t
  describe(i)
  i.M()

  i = &T{"hello"}
  describe(i)
  i.M()
}

func describe(i I) {
  fmt.Printf("(%v, %T)\n", i, i)
}

Вывод:

(, *main.T)

(&{hello}, *main.T)
hello

Интерфейсное значение nil

Интерфейсное значение nil не содержит ни значения, ни конкретного типа.

Вызов метода у nil интерфейса является ошибкой времени выполнения, т.к. внутри пары значение/тип нет типа, чтобы указать, какой конкретный метод надо вызвать. (В примере приводится ошибка при исполнении кода в песочнице)

package main

import "fmt"

type I interface {
  M()
}

func main() {
  var i I
  describe(i)
  i.M()
}

func describe(i I) {
  fmt.Printf("(%v, %T)\n", i, i)
}

Вывод:

(, )
panic: runtime error: 
invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation 
code=0xffffffff addr=0x0 pc=0xd5ee4]

goroutine 1 [running]:
main.main()
  /tmp/sandbox948166973/main.go:12 +0x24

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

Интерфейс, который не содержит ни одного метода называется пустым интерфейсом:

interface{}

Пустой интерфейс может содержать значение любого типа. (Каждый тип реализует по меньшей мере нуль методов.)

Пустые интерфейсы используются в коде, где необходимо работать со значениями неизвестного типа. Например, fmt.Print принимает любое количество аргументов типа interface{}.

package main

import "fmt"

func main() {
  var i interface{}
  describe(i)

  i = 42
  describe(i)

  i = "hello"
  describe(i)
}

func describe(i interface{}) {
  fmt.Printf("(%v, %T)\n", i, i)
}

Вывод:

(, )
(42, int)
(hello, string)


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


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

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