54.蛤蟆笔记go语言——interface使用

notbaron · · 1180 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

54.蛤蟆笔记go语言——interface使用

         Go语言中使用interface是比较困难的。使用基本比较简单,但是设计自己的interface就比较困难了。所以如何高效使用interface很有必要。

什么是interface

         一个interface包含两个东西:一组方法(也是类型),或类型。

         例如一个animal 类型可以是一个接口。可以定义animal为任何可以说话的。

type Animal interface {

    Speak() string

}

         这样定义了animal,可以是任何包含speak方法的类型。

         Speak没有任何参数,返回一个字符串。任何定义了该方法的类型都满足animal接口。

         没有关键字来指定类型是否满足接口,这个是自动实现的。创建一对类型来满足这个接口。

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

}

这样有4个类型的animals:一个狗、一个猫、一个llama和一个java程序员。

主函数如下:

func main() {

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

    for _, animal:= range animals {

       fmt.Println(animal.Speak())

    }

}

运行如下:

Woof!

Meow!

?????

Designpatterns!

Interface{}类型

Interface{}类型是空的接口。很多疑惑的根源。

空的接口没有方法。因为没有implemets关键字,所以至少有0个方法的类型都自动满足空接口。PS:是至少有0个,呵呵,就是任何时候都成立了哦。

         意味着,如果写了一个函数将interface{}作为接口,那么这个函数可以接受任何值

如下函数:

func DoSomething(v interface{}) {

   // ...

}

接受任何参数。

         那么问题来了,在函数体中v是什么类型呢?是不是任何类型呢?这是不对的,v不是任何类型,而是interface{}类型。任何值在运行时候都有确定的类型,v就是interface{}类型。

         其实一个interface{}值存储的2个数据字节。一个用于指向类型的方法表,另一个指向这个值实际保存的数据。如果理解interface值是2个字节包含指针指向数据,就会避免很多陷阱。对于interface接口的实现,可以参考后面的友情链接。

         在上一个例子中构建animal类型的slice,不需要使用Animal(Dog{})说明Dog类型,在animal的slice中,每个元素都是animal类型,但是不同的值有不同的类型。

         不知道interface如何在内存中存储的确会迷惑。例如,可以将[]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)

}

运行报错如下:

cannotuse names (type []string) as type []interface {} in argument to PrintAll

不能把[]string转换为[]interface.

如果想要工作,需要将[]string转换为[]interface{}

如下:

packagemain

 

import(

    "fmt"

)

 

funcPrintAll(vals[]interface{}){

    for_,val:=rangevals{

        fmt.Println(val)

    }

}

 

funcmain(){

    names:=[]string{"stanley","david","oscar"}

    vals:=make([]interface{},len(names))

    fori,v:=rangenames{

        vals[i]=v

    }

    PrintAll(vals)

}

执行如下:

stanley

david

oscar

这个的确不是很完美,但是实际上[]interface{}很少使用。

指针和接口

另一个接口的细节是接口定义。定义没有描述是否使用指针接受还是值接受来实现接口。当给出的是一个接口值,不能保证类型是不是一个指针。在之前的例子中,定义所有的方法是值接收,赋值给animal的sclie. 我们来改变一下猫的Speak()方法为指针接收如下:

func (c *Cat) Speak() string {

    return "Meow!"

}

如果允许就会报错如下:

cannot use Cat literal (typeCat) as type Animal in array or slice literal:

Catdoes not implement Animal (Speak method has pointer receiver)

可以通过将*Cat指针指向animal slice来替代 Cat值。还用new(Cat)来代替Cat{}

如:

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

         OK,继续。

         传递*Dog指针代替Dog值,但是不改变Dog的Speak函数。

如下:

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

也可以正常工作。但是小许不同的是,不需要改变Speak方法的接收类型。这是因为指针类型可以方法相关的方法,但是反过来是不可以的。*Dog可以使用Speak方法,但是一个Cat不能访问*Cat的Speak.

         我们要切记的是: Go中传递的任何东西都是值。每次你调用一个函数,会传递数据的副本。

func (t T)MyMethod(s string) {

    // ...

}

函数类型是func(T,string),函数值通过值来传递。

在方法定义的接收器值类型的改变不会被调用者看见,因为调用者是看见完整隔离的Dog值。

         既然所有东西都是通过值来传递,那么明显*Cat方法不能被cat值使用,任何Cat值可能有任何的*Cat指针来指向。如果想通过使用Cat值来调用一个*Cat方法,不能使用*Cat指针开始。相反,如果有一个基于Dog类型的方法,我们有一个*Dog指针,通过*Dog指针可以明确指向一个Dog值,那么Go runtime会在需要的时候将指针关联到Dog值。所以,给出一个*Dog值,和一个Dog类型的方法,就可以调用Dog的方法。

友情链接

http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go

http://www.laktek.com/2012/02/13/learning-go-interfaces-reflections/

 

 

 

 


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:notbaron

查看原文:54.蛤蟆笔记go语言——interface使用

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1180 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传