android 空界面

An interface in Go specifies a method set. Any type that implements this method set is said to implement the interface.

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

An interface is implemented implicity. There is no implements keyword, used in other languages. If a type defines the entire method set of an interface, it implements it. It’s called structural typing, the compile-time equivalent of duck typing.

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

If it walks like duck, swims like a duck and quacks like a duck, it’s a duck

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

Important: in Go, interface abstract a set of methods (actions), not data.

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

(Example)

The following example taken from How to use interfaces in Go illustrates a basic interface of an Animal that can Speak, and different structs that satisfy the interface:

下面的示例摘自如何在Go中使用接口,该示例说明了会说话的Animal的基本接口以及满足该接口的不同结构:

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!"
}

You can build an []Animal and run Speak() on its content:

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

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

play

空界面 (The empty interface)

The Animal interface is small and nice, but there’s something even simpler, yet somewhat a complex concept to understand for beginners: the empty interface.

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

interface{} is the Go empty interface, a key concept. Every type implements it by definition.

interface{}是Go空接口,这是一个关键概念。 每种类型均按定义实现。

An interface is a type, so you can define for example:

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

type Dog struct {
    Age interface{}
}

and you can also pass an empty interface type as a function parameter:

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

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

Accepting interface{} doesn’t mean the function accepts any type, but rather means that Eat accepts a value of interface{} type.

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

At runtime, Go will convert the actual value passed to an interface{} value.

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

If you define a field in a struct with type interface{}, you can assign it a value of any type. For example:

如果您在类型为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)

    dog.Age = 3
    fmt.Printf("%#v %T\n", dog.Age, dog.Age)

    dog.Age = "not really an age"
    fmt.Printf("%#v %T", dog.Age, dog.Age)
}

play

prints

版画

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

Of course this is not limited to basic types: you could store a slice, a map, any custom struct in there.

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

接口如何在内部表示 (How interface are represented internally)

Internally, an interface value is two words, sets of bytes.

在内部,接口是两个words ,即字节组。

One word points to the value underlying type. One word points to the data.

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

转换次数 (Conversions)

When we did animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}} previously, Go automatically converted all our specific animal types to an Animal type. Every element of that slice is now type Animal, with the underlying type pointing to the specific species, like Dog, Cat and Llama.

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

But if a method accepts []interface{} for example, we cannot just pass animals to it, because the type does not match. We need to explicitly convert each Animal to an interface{} before, with a loop, like described in the Can I convert a []T to an []interface{} FAQ:

但是,例如,如果某个方法接受[]interface{} ,则我们不能仅将animals传递给它,因为类型不匹配。 我们需要先使用循环将每个Animal显式转换为interface{} ,如“ 我可以将[] T转换为[] interface {}常见问题解答”中所述。

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

确定interface{}的基础类型interface{} (Determine the underlying type of an interface{})

If a value is of type interface{}, you might want to determine the underlying type. How? With a switch on .(type), like Effective Go explains:

如果值的类型为interface{} ,则可能要确定基础类型。 怎么样? 启用.(type) ,如Effective Go所述

A switch can also be used to discover the dynamic type of an interface variable. Such a type switch uses the syntax of a type assertion with the keyword type inside the parentheses. If the switch declares a variable in the expression, the variable will have the corresponding type in each clause. It’s also idiomatic to reuse the name in such cases, in effect declaring a new variable with the same name but a different type in each case.

开关也可以用来发现接口变量的动态类型。 这种类型开关使用括号内带有关键字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
}

翻译自: https://flaviocopes.com/go-empty-interface/

android 空界面

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐