Это 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, см. больше замечательных статей.
Публичная учетная запись «Голанг идет» подготовила для вас подарочный пакет для изучения тайны, и ответьте на [электронную книгу] в фоновом режиме, чтобы получить его!