Это 19-я статья из серии "Изучаем язык Go".
что такое интерфейс
В некоторых объектно-ориентированных языках программирования, таких как Java, PHP и т. д., интерфейс определяет поведение объекта и указывает только то, что объект должен делать. Точная реализация поведения зависит от объекта.
В языке Go интерфейс — это набор методов, но он не содержит реализации методов, он абстрактен, а интерфейс не может содержать переменных. Говорят, что тип T реализует интерфейс, когда он предоставляет определения всех методов интерфейса. Интерфейс указывает, какие методы должен иметь тип, а тип решает, как реализовать эти методы.
объявление интерфейса
Объявление интерфейса похоже на структуру, используя псевдонимы типов и требуя интерфейс ключевого слова Синтаксис следующий:
type Name interface {
Method1(param_list) return_type
Method2(param_list) return_type
...
}
На самом деле определить интерфейс:
type Shape interface {
Area() float32
}
Приведенный выше код определяет тип интерфейса Shape, который содержит метод Area(), не принимающий параметров и возвращающий float32. Любой тип T, который реализует метод Area(), мы говорим, что он реализует интерфейс Shape.
type Shape interface {
Area() float32
}
func main() {
var s Shape
fmt.Println("value of s is", s)
fmt.Printf("type of s is %T\n", s)
}
value of s is <nil>
type of s is <nil>
В приведенном выше коде, поскольку интерфейс является типом, может быть создана переменная s типа Shape Вам интересно, почему тип s равен nil? Переходим к следующему разделу!
значение типа интерфейса
статическая и динамическая типизация
Тип переменной указывается в момент объявления и не может быть изменен, что называется статическим типом. Статический тип интерфейса — это сам интерфейс. Интерфейс не имеет статических значений, он указывает на динамические значения. Переменная типа интерфейса хранит значение типа, реализующего интерфейс. Значение является динамическим значением интерфейса, а тип, реализующий интерфейс, является динамическим типом интерфейса.
type Iname interface {
Mname()
}
type St1 struct {}
func (St1) Mname() {}
type St2 struct {}
func (St2) Mname() {}
func main() {
var i Iname = St1{}
fmt.Printf("type is %T\n",i)
fmt.Printf("value is %v\n",i)
i = St2{}
fmt.Printf("type is %T\n",i)
fmt.Printf("value is %v\n",i)
}
type is main.St1
value is {}
type is main.St2
value is {}
Статический тип переменной i — Iname и не может быть изменен. Динамический тип не фиксирован.После первого выделения динамический тип i — St1, после второго выделения динамический тип i — St2, а все динамические значения — пустые структуры.
Иногда динамическая типизация интерфейса называетсяконкретный тип, когда мы обращаемся к типу интерфейса, возвращается тип базового динамического значения.
нулевое значение интерфейса
Давайте посмотрим на пример:
type Iname interface {
Mname()
}
type St struct {}
func (St) Mname() {}
func main() {
var t *St
if t == nil {
fmt.Println("t is nil")
} else {
fmt.Println("t is not nil")
}
var i Iname = t
fmt.Printf("%T\n", i)
if i == nil {
fmt.Println("i is nil")
} else {
fmt.Println("i is not nil")
}
fmt.Printf("i is nil pointer:%v",i == (*St)(nil))
}
t is nil
*main.St
i is not nil
i is nil pointer:true
Разве не удивительно, что значение, которое мы присваиваем переменной i, равно нулю, хотя i не равно нулю. Давайте посмотрим, что происходит!
Динамические типы были упомянуты выше, динамические значения — это значения, которые фактически выделены. Помните это:Значение типа интерфейса равно нулю тогда и только тогда, когда и динамическое значение, и динамический тип равны нулю.. В приведенном выше коде после присвоения значения переменной i динамическое значение i равно нулю, но динамический тип — *St, а i — нулевой указатель, поэтому условие равенства не выполняется.
Взгляните на спецификацию языка Go:
var x interface{} // x is nil and has static type interface{}
var v *T // v has value nil, static type *T
x = 42 // x has value 42 and dynamic type int
x = v // x has value (*T)(nil) and dynamic type *T
Изучив этот раздел, я полагаю, вы уже знаете, почему выходной тип s переменной типа Shape в предыдущем разделе равен nil, поскольку при объявлении var s Shape динамический тип s равен nil.
реализовать интерфейс
См. пример:
type Shape interface {
Area() float32
}
type Rect struct {
width float32
height float32
}
func (r Rect) Area() float32 {
return r.width * r.height
}
func main() {
var s Shape
s = Rect{5.0, 4.0}
r := Rect{5.0, 4.0}
fmt.Printf("type of s is %T\n", s)
fmt.Printf("value of s is %v\n", s)
fmt.Println("area of rectange s", s.Area())
fmt.Println("s == r is", s == r)
}
type of s is main.Rect
value of s is {5 4}
area of rectange s 20
s == r is true
Приведенный выше код создает интерфейс Shape, структуру Rect и метод Area(). Так как Rect реализует все методы, определенные интерфейсом, хотя есть только один, Rect реализует интерфейс Shape.
В основной функции создается переменная s типа интерфейса со значением nil и инициализируется структурой типа Rect, которая допустима, поскольку структура Rect реализует интерфейс. После присваивания динамический тип s становится Rect, а динамическое значение — значением структуры {5.0, 4.0}.
можно использовать напрямую.Синтаксис вызывает метод Area(), поскольку конкретный тип s — Rect, а Rect реализует метод Area().
пустой интерфейс
Интерфейс, который не содержит никаких методов, называется пустым интерфейсом, например: interface{}. Поскольку пустой интерфейс не содержит никаких методов, любой тип по умолчанию реализует пустой интерфейс.
Например, функция Println() в пакете fmt может принимать несколько типов значений, таких как: int, string, array и т. д. Почему, потому что его формальный параметр является интерфейсным типом, который может принимать значения любого типа.
func Println(a ...interface{}) (n int, err error) {}
Давайте посмотрим на пример:
type MyString string
type Rect struct {
width float32
height float32
}
func explain(i interface{}) {
fmt.Printf("type of s is %T\n", i)
fmt.Printf("value of s is %v\n\n", i)
}
func main() {
ms := MyString("Seekload")
r := Rect{5.0, 4.0}
explain(ms)
explain(r)
}
type of s is main.MyString
value of s is Seekload
type of s is main.Rect
value of s is {5 4}
Приведенный выше код создает пользовательский тип строки MyString , структуру Rect и функцию объяснения(). Формальный параметр функции объяснения() — это пустой интерфейс, поэтому он может принимать значения любого типа.
Это первая часть про использование интерфейса, и я открою статью, чтобы рассказать вам об остальном, продолжайте обращать внимание!
(Конец полного текста)
Оригинал статьи, если нужно перепечатать, указывайте источник!
Добро пожаловать, чтобы отсканировать код и подписаться на официальный аккаунт »Голанг здесь” или двигатьсяseekload.net, см. больше замечательных статей.
Публичная учетная запись «Голанг идет» подготовила для вас подарочный пакет для изучения тайны, и ответьте на [электронную книгу] в фоновом режиме, чтобы получить его!