Оказывается, это Go Interface

Go
Оказывается, это Go Interface

определение

  • Интерфейс представляет собой набор сигнатур методов, определенных для определения поведения объекта.Если объект достигает поведения, определенного методом, установленным в интерфейсе, можно сказать, что он реализует интерфейс;

  • Эти методы могут быть реализованы разными объектами в разных местах, и эти реализации могут иметь разное поведение;

  • Основная задача интерфейса — предоставить сигнатуры имен методов, входные параметры и возвращаемые типы. Наконец, метод реализуется конкретным объектом, например struct;

  • Значение инициализации интерфейса равно нулю;

  • Используйте ключевое слово type, чтобы объявить, что интерфейс представляет тип, а набор сигнатур методов интерфейса определяется в фигурных скобках.

    type Animal interface {
    	Bark() string
    	Walk() string
    }
    

    Таким образом, Dog реализует интерфейс Animal, поэтому экземпляр Animal можно использовать для получения экземпляра Dog.Он должен одновременно реализовывать методы Bark() и Walk(), иначе нельзя считать, что он реализует Animal. интерфейс.

    type Dog struct {
    	name string
    }
    
    func (dog Dog) Bark() string {
    	fmt.Println(dog.name + ":wan wan wan!")
    	return "wan wan wan"
    }
    
    func (dog Dog) Walk() string {
    	fmt.Println(dog.name + ":walk to park!")
    	return "walk to park"
    }
    
    func main() {
    	var animal Animal
    
    	fmt.Println("animal value is:", animal)	//animal value is: <nil>
    	fmt.Printf("animal type is: %T\n", animal) //animal type is: <nil>
    
    	animal = Dog{"旺财"}
    	animal.Bark() //旺财:wan wan wan!
    	animal.Walk() //旺财:walk to park!
    
    	fmt.Println("animal value is:", animal) //animal value is: {旺财}
    	fmt.Printf("animal type is: %T\n", animal) //animal type is: main.Dog
    }
    

nil interface

В приведенном выше примере мы печатаем животное, которое только что определили:

  • значение равно нулю
  • тип тоже ноль

Официальное определение: значения интерфейса с нулевыми базовыми значениями:

  • Только объявите, что неназначенный интерфейс является нулевым интерфейсом, а значение и тип равны нулю.
  • Пока присваивание выполнено, даже если значению присваивается нулевой тип, это больше не нулевой интерфейс.
type I interface {
	Hello()
}

type S []int

func (i S) Hello() {
	fmt.Println("hello")
}
func main() {
	var i I
	fmt.Printf("1:i Type:%T\n", i)
	fmt.Printf("2:i Value:%v\n", i)

	var s S
	if s == nil {
		fmt.Printf("3:s Value%v\n", s)
		fmt.Printf("4:s Type is %T\n", s)
	}

	i = s
	if i == nil {
		fmt.Println("5:i is nil")
	} else {
		fmt.Printf("6:i Type:%T\n", i)
		fmt.Printf("7:i Value:%v\n", i)
	}
}

output:

	1:i Type:<nil>
	2:i Value:<nil>
	3:s Value[]
	4:s Type is main.S
	6:i Type:main.S
	7:i Value:[]

Из результата инициализированная переменная i является нулевым интерфейсом, когда переменной s со значением nil присваивается i, i больше не является нулевым интерфейсом.
Внимательные школьники найдут деталь, третью строчку вывода

3:s Value[]

Очевидно, что значение s равно nil, но вывод представляет собой []. Это связано с тем, что fmt использует отражение для определения содержимого печати. ​​Поскольку тип s — срез, fmt представлен [].

empty interface

Go допускает интерфейсы без каких-либо методов, такой тип интерфейса называется пустым интерфейсом. Все типы реализуют пустой интерфейс, потому что любой тип реализует как минимум 0 методов.
Типичным сценарием приложения является метод Println пакета fmt, который поддерживает получение различных типов данных и вывод их на консоль, что является заслугой interface{}. Рассмотрим случай ниже:

func Print(i interface{}) {
	fmt.Println(i)
}
func main() {
	var i interface{}
	i = "hello"
	Print(i)
	i = 100
	Print(i)
	i = 1.29
	Print(i)
}

Тип параметра метода Print — interface{}, который можно получить, когда мы передаем string, int, float и другие типы.
Хотя interface{} может принимать параметры любого типа, может ли срез типа interface{} принимать любой тип слайса. Следующий код вызовет паническую ошибку,

var dataSlice []int = foo()
var interfaceSlice []interface{} = dataSlice
// cannot use dataSlice (type []int) as type []interface { } in assignment

По определенным причинам официальный сайт вики (GitHub.com/gowaves/go/me…) описан, и общий смысл в том, что есть две причины ошибки:

  • []interface{} — это не интерфейс, это слайс, но элементы слайса — это интерфейсы
  • Размер памяти типа []interface{} определяется во время компиляции (N*2), в то время как размер других типов слайсов равен N * sizeof(MyType), поэтому тип []MyType не выделяется быстро для [] ]интерфейс{}.

Определите, какой тип хранит переменная интерфейса

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

t := i.(T)

Этот оператор утверждает, что значение интерфейса i содержит конкретный тип T, и присваивает базовое значение T переменной t. Если значение, хранящееся в i, не относится к типу T, будет вызвана паническая ошибка. Чтобы избежать панических ошибок, вы можете выполнить проверку утверждений, выполнив следующие действия:

t, ok := i.(T)

Утверждение выполнено успешно, значение ok равно true, утверждение не выполнено, значение t является нулевым значением типа T, и панической ошибки не возникает.

func main() {
	var i interface{}
	i = "hello"

	s := i.(string)
	fmt.Println(s)

	s, ok := i.(string)
	fmt.Println(s, ok)

	f, ok := i.(float64)
	fmt.Println(f, ok)

	i = 100
	t, ok := i.(int)
	fmt.Println(t, ok)

	t2 := i.(string) //panic
	fmt.Println(t2)
}

Type switch

Другой удобный способ определить конкретный тип переменной интерфейса — использовать оператор switch. Следующее:

func Print(i interface{}) {
	switch i.(type) {
	case string:
		fmt.Printf("type is string,value is:%v\n", i.(string))
	case float64:
		fmt.Printf("type is float32,value is:%v\n", i.(float64))
	case int:
		fmt.Printf("type is int,value is:%v\n", i.(int))
	}
}
func main() {
	var i interface{}
	i = "hello"
	Print(i)
	i = 100
	Print(i)
	i = 1.29
	Print(i)
}

Гибкий и эффективный динамический тип интерфейса позволяет языку Go гибко и безопасно преобразовывать различные совместимые типы, сохраняя при этом безопасность и эффективность сильных статических типов.

Оригинал с моего Github

Ссылаться на

Golang print nil
InterfaceSlice