Go语言学习笔记---接口

Mervyn1205 · · 730 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。
下面定义了具有一个字段和两个方法的结构类型S
代码6.1
type S struct { i int }
func (p *S) Get() int { return p.i }
func (p *S) Put(v int) { p.i = v }

也可以定义接口类型,仅仅是方法的集合。这里定义了一个有两个方法的接口I:
type I interface {
    Get() int
    Put(int)
}
对于接口I,S是合法的实现,因为它定义了I所需的两个方法。注意,即便是没有明确定义S实现了I,这也是正确的。

接口值:
例:
func f(p I) {                //定义一个函数接受一个接口类型作为参数
    fmt.Println(p.Get())     //p实现了接口I,必须有Get()方法
    p.Put(1)                 //Put()方法是类似的
}

这里的变量p保存了接口类型的值。因为S实现了I,可以调用f向其传递S类型的值的指针:
var s S; f(&s)
获取s的地址,而不是S的值的原因,是因为在s的指针上定义了方法,参阅上面的代码6.1。这并不是必须的——可以定义让方法接受值——但是这样的话Put方法就不会像期望的那样工作了。
实际上,无须明确一个类型是否实现了一个接口意味着Go实现了叫做duck typing的模式。这不是纯粹的duck typing,因为如果可能的话Go编译器将对类型是否实现了接口进行实现静态检查。
假设需要在函数f中知道实际的类型。在Go中可以使用type switch得到。
func f(p I) {
switch t := p.(type) {    //类型判断。在switch语句中使用(type)。保存类型到变量t;
case *S:             //p的实际类型是S的指针;
case *R:             //p的实际类型是R的指针;
case S:              //p的实际类型是S;
case R:              //p的实际类型是R;
default:             //实现了I的其他类型。
}
}

注意: element.(type)语法不能在switch外的任何逻辑里面使用,如果你要在switch外面判断一个类型就使用 comma-ok。

类型判断不是唯一的运行时得到类型的方法。为了在运行时得到类型,同样可以使用“comma, ok”来判断一个接口类型是否实现了某个特定接口:
if t, ok := something.(I); ok {
// 对于某些实现了接口I 的
// t 是其所拥有的类型
}

Comma-ok断言

Go语言里面有一个语法,可以直接判断是否是该类型的变量: value, ok = element.(T),这里value就是变量的值,ok是一个bool类型,element是interface变量,T是断言的类型。

如果element里面确实存储了T类型的数值,那么ok返回true,否则返回false。

例:
package main

import (
    "fmt"
    "strconv"
)

type Element interface{}
type List [] Element

type Person struct {
    name string
    age int
}

//定义了String方法,实现了fmt.Stringer
func (p Person) String() string {
    return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
}

func main() {
    list := make(List, 3)
    list[0] = 1 // an int
    list[1] = "Hello" // a string
    list[2] = Person{"Dennis", 70}

    for index, element := range list {
        if value, ok := element.(int); ok {
            fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
        } else if value, ok := element.(string); ok {
            fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
        } else if value, ok := element.(Person); ok {
            fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
        } else {
            fmt.Println("list[%d] is of a different type", index)
        }
    }
}
输出结果:
list[0] is an int and its value is 1
list[1] is a string and its value is Hello
list[2] is a Person and its value is (name: Dennis - age: 70 years)


确定一个变量实现了某个接口,可以使用:
t := something.(I)

空接口
由于每个类型都能匹配到空接口:interface{}。我们可以创建一个接受空接口作为参数的普通函数:
Listing 6.2.用空接口作为参数的函数
func g(something interface{}) int {
    return something.(I).Get()
}
在这个函数中的return something.(I).Get()是有一点窍门的。值something具有类型interface{},这意味着方法没有任何约束:它能包含任何类型。.(I)是类型断言,用于转换something到I类型的接口。如果有这个类型,则可以调用Get()函数。因此,如果创建一个*S类型的新变量,也可以调用g(),因为*S同样实现了空接口。
s = new(S)
fmt.Println(g(s));
例:
package main
import (
    "fmt"
)
func main(){
    s  := new(S)
    ss := "Hello world"
    fmt.Printf("%d\n",g(s))
    fmt.Printf("%s\n",demo(ss))
}
func g(something interface{}) int {
    return something.(I).Get()                //?为什么不是something.(S).Get()
}
func demo(something interface{}) string {
    return something.(string)
}
type I interface {
    Get() int
    Put(int)
}
type S struct { i int }
func (p *S) Get() int {
    return p.i
}
func (p *S) Put(v int) {
    p.i = v
}

输出结果:
0
Hello world

例2:
package main
import (
    "fmt"
)
func main(){
    s  := S{1}
    fmt.Printf("%d\n",g(s))
}
func g(something S) int {
    return something.Get()
}
type S struct { i int }
func (p *S) Get() int {
    return p.i
}
func (p *S) Put(v int) {
    p.i = v
}

输出结果:
1

接口名字
根据规则,单方法接口命名为方法名加上-er后缀:Reader,Writer,Formatter等。
有一堆这样的命名,高效的反映了它们职责和包含的函数名。Read,Write,Close,Flush,String等等有着规范的声明和含义。为了避免混淆,除非有类似的声明和含义,否则不要让方法与这些重名。相反的,如果类型实现了与众所周知的类型相同的方法,那么就用相同的名字和声明;将字符串转换方法命名为String而不是ToString。



有疑问加站长微信联系

本文来自:CSDN博客

感谢作者:Mervyn1205

查看原文:Go语言学习笔记---接口

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

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