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

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

           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!

?????

Design patterns!

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)

}

运行报错如下:

cannot usenames (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:

Cat does not implementAnimal (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/

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值