转到空界面说明

接口,接口{},转换

Go中的接口指定一个方法集。任何实现此方法集的类型都被称为实施接口

接口是隐式实现的。没有贯彻关键字,在其他语言中使用。如果类型定义了接口的整个方法集,则它将实现它。这称为结构化类型,相当于编译时鸭打字

如果它走路像鸭子,像鸭子一样游泳,像鸭子一样嘎嘎叫,那就是鸭子

重要:在Go中,接口抽象了一组方法(动作),而不是数据。

例子

以下示例取自如何在Go中使用界面说明了可以说话的动物的基本界面,以及满足该界面的不同结构:

type Animal interface {
    Speak() string
}

type Dog struct { }

func (d Dog) Speak() string { return “Woof!” }

type Cat struct { }

func (c Cat) Speak() string { return “Meow!” }

type Llama struct { }

func (l Llama) Speak() string { return “???” }

type JavaProgrammer struct { }

func (j JavaProgrammer) Speak() string { return “Design patterns!” }

您可以构建[]动物并运行Speak()在其内容上:

func main() {
    animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}
    for _, animal := range animals {
        fmt.Println(animal.Speak())
    }
}

空界面

Animal界面虽小又漂亮,但对于初学者来说,还有一些更简单但又有些复杂的概念需要理解:空界面。

interface{}Go空界面是一个关键概念。每种类型都通过定义实现它。

接口是一种类型,因此您可以定义例如:

type Dog struct {
    Age interface{}
}

并且您还可以将空接口类型作为函数参数传递:

func Eat(t interface{}) {
    // ...
}

接受interface{}并不意味着该函数接受任何类型,而是意味着Eat接受一个值interface{}类型。

在运行时,Go会将传递的实际值转换为interface{}价值。

如果您在类型为struct的字段中定义字段interface{},您可以为其分配任何类型的值。例如:

package main

import “fmt”

type Dog struct { Age interface{} }

func main() { dog := Dog{} dog.Age = “3” fmt.Printf("%#v %T\n", dog.Age, dog.Age)

<span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span> = <span style="color:#ae81ff">3</span>
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">"%#v %T\n"</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>)

<span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span> = <span style="color:#e6db74">"not really an age"</span>
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">"%#v %T"</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>)

}

印刷

"3" string
3 int
"not really an age" string

当然,这不限于基本类型:您可以在其中存储切片,地图和任何自定义结构。

接口如何在内部表示

在内部,一个接口价值是两个words,字节组。

一个词指向基础类型的值。一个字指向数据。

转换次数

当我们做animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}以前,Go会自动将我们所有的特定动物类型转换为动物类型。现在,该切片的每个元素都是Animal类型,其下层类型指向特定的物种,例如Dog,Cat和Llama。

但是如果一个方法接受[]interface{}例如,我们不能只是通过animals到它,因为类型不匹配。我们要明确地转换每个Animalinterface{}之前,有一个循环,如我可以将[] T转换为[]接口{}常问问题:

t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
    s[i] = v
}

确定基础类型interface{}

如果值是类型interface{},您可能要确定基础类型。如何?打开电源.(type), 喜欢有效执行解释:

开关也可以用来发现接口变量的动态类型。这种类型开关使用括号内带有关键字type的类型断言的语法。如果开关在表达式中声明了变量,则该变量在每个子句中将具有相应的类型。在这种情况下重用名称也是惯用的,实际上是在每种情况下声明一个具有相同名称但类型不同的新变量。

var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
default:
    fmt.Printf("unexpected type %T\n", t)     // %T prints whatever type t has
case bool:
    fmt.Printf("boolean %t\n", t)             // t has type bool
case int:
    fmt.Printf("integer %d\n", t)             // t has type int
case *bool:
    fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
case *int:
    fmt.Printf("pointer to integer %d\n", *t) // t has type *int
}

更多教程: