go interface

interface

在面向对象编程中,可以这么说:“接口定义了对象的行为”, 那么具体的实现行为就取决于对象了。

在Go中,接口是一组方法签名(声明的是一组方法的集合)。

当一个类型为接口中的所有方法提供定义时,它被称为实现该接口。

它与oop非常相似。接口指定类型应具有的方法,类型决定如何实现这些方法。

在 java 或者 C++这类 OOP 语言中,一个类型实现了某个接口,体现在代码层面的意思是,你说你实现了某个接口,那你必须定义接口中的所有方法.

在 Golang 中,反过来说,如果一个类为某个接口中的方法提供了定义,那么就说这个类实现了那个接口.

让我们来看看这个例子: Animal 类型是一个接口,我们将定义一个 Animal 作为任何可以说话的东西。这是 Go 类型系统的核心概念:我们根据类型可以执行的操作而不是其所能容纳的数据类型来设计抽象。

type Animal interface{
        Speak() string
}

非常简单:我们定义 Animal 为任何具有 Speak 方法的类型。Speak 方法没有参数,返回一个字符串。所有定义了该方法的类型我们称它实现了 Animal 接口。Go 中没有 implements 关键字,判断一个类型是否实现了一个接口是完全是自动地。让我们创建几个实现这个接口的类型:

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

我们现在有四种不同类型的动物:DogCatLlamaJavaProgrammer。在我们的 main 函数中,我们创建了一个 []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}} ,看看每只动物都说了些什么:

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

interface{} 类型


interface{} 类型,空接口,是导致很多混淆的根源。

go语言种interface{}就是一个独立的类型,interface{}interface没有关系,为什么interface{}不使用any作为关键字?


  • interface{}就是一个独立的类型

interface{} 类型是没有方法的接口(而且也没有名称!),所有类型都至少实现了 0 个方法,所以 所有类型都实现了空接口。这意味着,如果您编写一个函数以 interface{} 值作为参数,那么您可以为该函数提供任何值。例如:

func DoSomething(v interface{}) {
        // ...
}

这里是让人困惑的地方:在 DoSomething 函数内部,v 的类型是什么?新手们会认为 v 是任意类型的,但这是错误的。v 不是任意类型,它是 interface{} 类型。

对的,没错!当将值传递给DoSomething 函数时,Go 运行时将执行类型转换(如果需要),并将值转换为 interface{} 类型的值。所有值在运行时只有一个类型,而 v 的一个静态类型是 interface{}

这可能让您感到疑惑:好吧,如果发生了转换,到底是什么东西传入了函数作为 interface{} 的值呢?(具体到上例来说就是 []Animal 中存的是啥?)

一个接口值由两个字(32 位机器一个字是 32 bits,64 位机器一个字是 64 bits)组成;一个字用于指向该值底层类型的方法表,另一个字用于指向实际数据。我不想没完没了地谈论这个。

在我们上面的例子中,当我们初始化变量 animals 时,我们不需要像这样Animal(Dog{}) 来显示的转型,因为这是自动地。这些元素都是 Animal 类型,但是他们的底层类型却不相同。

为什么这很重要呢?理解接口是如何在内存中表示的,可以使得一些潜在的令人困惑的事情变得非常清楚。比如,像 “我可以将 []T 转换为 []interface{}吗?” 这种问题就容易回答了。下面是一些烂代码的例子,它们代表了对 interface{} 类型的常见误解:

package main
import(
        "fmt"
        )
func PrintAll(vals []interface{}) {
        for _, val := range vals {
                fmt.Println(val)
                }
}
func main() {
        names := []string{ "stanley" , "david", "oscar"} 
        PrintAll(names)
}

运行这段代码你会得到如下错误:cannot use names (type []string) as type []interface {} in argument to PrintAll。如果想使其正常工作,我们必须将 []string 转为 []interface{}

package main 
import(
        "fmt"
        )
        
func PrintAll(vals []interface{}) {
        for_, val := range vals {
                fmt.Println(val)
        }
}
func main() {
        names := []string{"stanley", "david", "oscar"}
        vals := make([]interface{}, len(names))
        for i, v := range names { 
                vals[i] = v
        }
        PrintAll(vals)
}

很丑陋,但是生活就是这样,没有完美的事情。(事实上,这种情况不会经常发生,因为 []interface{} 并没有像你想象的那样有用)

指针和接口


接口的另一个微妙之处是接口定义没有规定一个实现者是否应该使用一个指针接收器或一个值接收器来实现接口。

当给定一个接口值时,不能保证底层类型是否为指针。在前面的示例中,我们将方法定义在值接收者之上。让我们稍微改变一下,将 CatSpeak() 方法改为指针接收器:

func(c *Cat) Speak() string {
    return "Meow!"
}

运行上述代码,会得到如下错误:

cannot use Cat literal (``type` `Cat) as ``type` `Animal in array or slice literal:``  ``Cat does not implement Animal (Speak method has pointer receiver)

该错误的意思是:你尝试将 Cat 转为 Animal ,但是只有 *Cat 类型实现了该接口。你可以通过传入一个指针 (new(Cat) 或者 &Cat{})来修复这个错误。

animals := []Animal{Dog{}, new(Cat), Llama{}, JavaProgrammer{}}

让我们做一些相反的事情:我们传入一个 *Dog 指针,但是不改变 DogSpeak() 方法:

animals := []Animal{new(Dog), new(Cat), Llama{}, JavaProgrammer{}}

这种方式可以正常工作,因为一个指针类型可以通过其相关的值类型来访问值类型的方法,但是反过来不行。即,一个 *Dog 类型的值可以使用定义在 Dog 类型上的 Speak() 方法,而 Cat 类型的值不能访问定义在 *Cat 类型上的方法。

这可能听起来很神秘,但当你记住以下内容时就清楚了:Go 中的所有东西都是按值传递的。每次调用函数时,传入的数据都会被复制。对于具有值接收者的方法,在调用该方法时将复制该值。例如下面的方法:

func (t T)MyMethod(s string) {
  // ...
} 

func(T, string) 类型的方法。方法接收器像其他参数一样通过值传递给函数。

因为所有的参数都是通过值传递的,这就可以解释为什么 *Cat 的方法不能被 Cat 类型的值调用了。任何一个 Cat 类型的值可能会有很多 *Cat 类型的指针指向它,如果我们尝试通过 Cat 类型的值来调用 *Cat 的方法,根本就不知道对应的是哪个指针。相反,如果 Dog 类型上有一个方法,通过 *Dog 来调用这个方法可以确切的找到该指针对应的 Gog 类型的值,从而调用上面的方法。运行时,Go 会自动帮我们做这些,所以我们不需要像 C语言中那样使用类似如下的语句 d->Speak()

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容

  • 1、interface 是一种类型 type I interface {Get() int} 首先interfac...
    Uzero阅读 2,609评论 0 2
  • interface 是一种类型,从它的定义可以看出来用了 type 关键字,更准确的说 interface 是一种...
    ze__lin阅读 639评论 0 0
  • 接口(interface)是一种抽象的类型,是对其他类型行为的概括和抽象。从语法角度来看,接口是一组方法签名定义的...
    JunChow520阅读 383评论 0 1
  • 今天研究一下interface https://mp.weixin.qq.com/s/EbxkBokYBajkCR...
    Winnifred_阅读 329评论 0 2
  •   如Go method中提及,Golang没有明确支持多态,但是通过其他手段可以实现类似C++中的多态特性,即本...
    北春南秋阅读 14,915评论 0 3