Подробное объяснение интерфейса языка Go (2)

Go
Подробное объяснение интерфейса языка Go (2)

Это 20-я статья из серии "Изучаем язык Go".

Напоминание: В конце статьи я оставил для всех небольшое упражнение, вы можете сначала прочитать статью, а затем выполнить упражнение, чтобы проверить свои результаты обучения!

Давайте продолжим предыдущую статью и продолжим разговор о других вариантах использования интерфейсов.

Реализовать несколько интерфейсов

Тип может реализовывать несколько интерфейсов, см. В следующем примере:

type Shape interface {
	Area() float32
}

type Object interface {
	Perimeter() float32
}

type Circle struct {
	radius float32
}

func (c Circle) Area() float32 {
	return math.Pi * (c.radius * c.radius)
}

func (c Circle) Perimeter() float32 {
	return 2 * math.Pi * c.radius
}

func main() {
	c := Circle{3}
	var s Shape = c
	var p Object = c
	fmt.Println("area: ", s.Area())
	fmt.Println("perimeter: ", p.Perimeter())
}

выход:

area:  28.274334
perimeter:  18.849556

В приведенном выше коде структура Circle реализует интерфейс Shape и интерфейс Object соответственно, поэтому структурная переменная c может быть присвоена переменным s и p. В настоящее время s и p имеют одинаковый динамический тип и динамическое значение, и вызывать соответствующие методы Area() и Perimeter(). Модифицируем программу:

fmt.Println("area: ", p.Area())
fmt.Println("perimeter: ", s.Perimeter())

Компиляция выдает ошибку:

p.Area undefined (type Object has no field or method Area)
s.Perimeter undefined (type Shape has no field or method Perimeter)

Почему? Поскольку статический тип s — это Shape, а статический тип p — Object. Есть ли какое-нибудь решение для этого? Да, давайте перейдем к следующему разделу

утверждение типа

Утверждения типа могут использоваться для получения базового значения интерфейса, обычный синтаксис:i.(Type), где я интерфейс и тип - это тип или интерфейс. При компиляционном времени он автоматически определяет, соответствует ли динамический тип I соответствует типу.

type Shape interface {
	Area() float32
}

type Object interface {
	Perimeter() float32
}

type Circle struct {
	radius float32
}

func (c Circle) Area() float32 {
	return math.Pi * (c.radius * c.radius)
}

func (c Circle) Perimeter() float32 {
	return 2 * math.Pi * c.radius
}

func main() {
	var s Shape = Circle{3}
	c := s.(Circle)
	fmt.Printf("%T\n",c)
	fmt.Printf("%v\n",c)
	fmt.Println("area: ", c.Area())
	fmt.Println("perimeter: ", c.Perimeter())
}

выход:

main.Circle
{3}
area:  28.274334
perimeter:  18.849556

В приведенном выше коде мы можем получить доступ к базовому значению interface s через c, а также вызвать методы Area() и Perimeter() через c, что решает описанные выше проблемы. В синтаксисе i.(Type), если Type не реализует интерфейс, к которому принадлежит i, во время компиляции будет сообщено об ошибке, или если динамическое значение i не равно Type, будет сообщено об ошибке паники. Как это решить? Можно использовать следующий синтаксис:

value, ok := i.(Type)

Используя приведенный выше синтаксис, Go автоматически обнаружит два случая, упомянутых выше, нам нужно только судить, правильный ли результат через переменную ok. Если это правильно, то ok равно true, в противном случае — false, а value — это нулевое значение, соответствующее Type.

Выбор типа

Выбор типа используется для сопоставления и сравнения конкретного типа интерфейса с несколькими типами, указанными в различных операторах case, что несколько похоже на оператор case switch, за исключением того, что тип, указанный в case, есть. Синтаксис выбора типа несколько похож на синтаксис утверждения типа: i.(type), где i — интерфейс, а type — фиксированное ключевое слово. Используйте это, чтобы получить конкретный тип интерфейса вместо значения. в каждом случае должен реализовать я интерфейс.

func switchType(i interface{}) {
	switch i.(type) {
	case string:
		fmt.Printf("string and value is %s\n", i.(string))
	case int:
		fmt.Printf("int and value is %d\n", i.(int))
	default:
		fmt.Printf("Unknown type\n")
	}
}
func main() {
	switchType("Seekload")
	switchType(27)
	switchType(true)
}

выход:

string and value is Seekload
int and value is 27
Unknown type

Приведенный выше код должен быть хорошо понят: в каком случае тип i соответствует, будет выполняться соответствующий оператор вывода.Примечание. Выбор типа возможен только для типов интерфейса.. Другие типы, такие как int, string и т. д., не допускаются:

i := 1
switch i.(type) {
case int:
	println("int type")
default:
	println("unknown type")
}

Ошибка:

cannot type switch on non-interface value i (type int)

вложенность интерфейса

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

type Math interface {
	Shape
	Object
}
type Shape interface {
	Area() float32
}

type Object interface {
	Perimeter() float32
}

type Circle struct {
	radius float32
}

func (c Circle) Area() float32 {
	return math.Pi * (c.radius * c.radius)
}

func (c Circle) Perimeter() float32 {
	return 2 * math.Pi * c.radius
}

func main() {

	c := Circle{3}
	var m Math = c
	fmt.Printf("%T\n", m )
	fmt.Println("area: ", m.Area())
	fmt.Println("perimeter: ", m.Perimeter())
}

выход:

main.Circle
area:  28.274334
perimeter:  18.849556

Приведенный выше код создает новый интерфейс Math путем вложения интерфейсов Shape и Object. Говорят, что любой тип, который реализует методы, определенные интерфейсами Shape и Object, также реализует интерфейс Math, например созданную нами структуру Circle. В основной функции определена переменная m интерфейсного типа, а динамическим типом является структура Circle.Обратите внимание на вызов методов следующих методов Area и Perimeter, которые аналогичны обращению к членам вложенных структур.

Реализация интерфейса с использованием приемников указателей и приемников значений

На переднем плане мы все реализуем интерфейс через приемник значений, на самом деле мы также можем реализовать интерфейс через приемник указателей. Есть еще на что обратить внимание в процессе реализации, давайте посмотрим:

type Shape interface {
	Area() float32
}

type Circle struct {
	radius float32
}

type Square struct {
	side float32
}

func (c Circle) Area() float32 {
	return math.Pi * (c.radius * c.radius)
}

func (s *Square) Area() float32 {
	return s.side * s.side
}

func main() {
	var s Shape
	c1 := Circle{3}
	s = c1
	fmt.Printf("%v\n",s.Area())

	c2 := Circle{4}
	s = &c2
	fmt.Printf("%v\n",s.Area())

	c3 := Square{3}
	//s = c3
	s = &c3
	fmt.Printf("%v\n",s.Area())

}

выход:

28.274334
50.265484
9

В приведенном выше коде структурный Circle реализует форму интерфейса через приемник значений. Мы уже обсуждали в методе, что значение значения получателя может использовать вызов значения или указателя, поэтому режим вызова C1 и C2 выше является допустимым.

Структура квадрата реализует форму интерфейса с приемником указателя. Если приведенный выше раздел комментария включен, компиляция не удастся:

cannot use c3 (type Square) as type Shape in assignment:
Square does not implement Shape (Area method has pointer receiver)

Из сообщения об ошибке видно, что мы пытаемся присвоить s тип значения c3, но c3 не реализует интерфейс Shape. Это может нас немного удивить, потому что внутри метода мы можем вызывать методы получателя указателя непосредственно с типом значения или типом указателя. Помните это:Допустимо вызывать метод получателя указателя с помощью указателя или адресуемого значения.. Однако конкретное значение, хранящееся в интерфейсе, не адресуется, и компилятор не может автоматически получить адрес c3, поэтому программа сообщает об ошибке.

Это краткое изложение использования интерфейса, надеюсь, эти две статьи помогут вам!

Операция:Утверждения типов, упомянутые в статье:i.(Type)Где i — интерфейс, Type может быть типом или интерфейсом Что означает выражение, если Type — это интерфейс? Каков следующий вывод программы?

type Shape interface {
	Area() float32
}

type Object interface {
	Perimeter() float32
}

type Circle struct {
	radius float32
}

func (c Circle) Area() float32 {
	return math.Pi * (c.radius * c.radius)
}

func main() {
	var s Shape = Circle{3}
	value1,ok1 := s.(Shape)
	value2,ok2 := s.(Object)

	fmt.Println(value1,ok1)
	fmt.Println(value2,ok2)
}

Добро пожаловать, чтобы оставить сообщение для обсуждения!


(Конец полного текста)

Оригинал статьи, если нужно перепечатать, указывайте источник!
Добро пожаловать, чтобы отсканировать код и подписаться на официальный аккаунт »Голанг здесь” или двигатьсяseekload.net, см. больше замечательных статей.

Публичная учетная запись «Голанг идет» подготовила для вас подарочный пакет для изучения тайны, и ответьте на [электронную книгу] в фоновом режиме, чтобы получить его!

公众号二维码