zoukankan      html  css  js  c++  java
  • Go 语言基础(六) 之 接口

    1、接口的定义
    保留字 interface 被赋予了多种不同的含义。
    每个类型都有接口,意 味着对那个类型定义了方法集合
    定义结构和结构的方法:
    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 的指针上定义了方法,
    这并不是必须的——可以定义让方法接受值——但是这样的话 Put 方法就不会像期望的那样工作了。
     
    Go 联合了接口值、静态类型检查、运行时动态转换,以及无须明确定义类型适 配一个接口
     
    定义另外一个类型同样实现了接口 I:
    type R struct { i int }
    func (p *R) Get() int { return p.i }
    func (p *R) Put(v int) { p.i = v }
     
    函数 f 现在可以接受 R 个 S 类型的变量。假设需要在函数 f 中知道实际的类型。
    在 Go 中可以使用 type switch 得到。
    func f(p I) {
    switcht:=p.(type){ // 类型判断。在 switch 语句中使用 (type)。保存类型到变量 t;
    case *S: // p的实际类型是S的指针;
    case *R: // p的实际类型是R的指针;
    case S: // p的实际类型是S;
    case R: // p的实际类型是R;
    default: // 实现了 I 的其他类型。
    }
    }
     
    在 switch 之外使用 (type) 是非法的。类型判断不是唯一的运行时得到类型的方法。
    为了在运行时得到类型,同样可以使用 “comma, ok” 来判断一个接口类型
    是否实现了某个特定接口:
    if t, ok := something.(I); ok {
    // 对于某些实现了接口 I 的
    // t 是其所拥有的类型
    }
     
    确定一个变量实现了某个接口,可以使用:
    t := something.(I)
     
     
    2、空接口
    由于每个类型都能匹配到空接口: interface{}。
    我们可以创建一个接受空接口 作为参数的普通函数:
    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));
    调用 g 的运行不会出问题,并且将打印 0。
    如果调用 g() 的参数没有实现 I 会带来一个麻烦:
    i:=5 ←声明i 是一个``该死的''int
    fmt.Println(g(i))
    这能编译,但是当运行的时候会得到:
    panic: interface conversion: int is not main.I: missing method Get
    这是绝对没问题,内建类型 int 没有 Get() 方法。
     
     
     
    3、方法
    方法就是有接收者的函数。
    可以在任意类型上定义方法(除了非本地类型,包括内建类型:int 类型不能有方法)。
    然而可以新建一个拥有方法的整数类型。
    type Foo int
    func (self Foo) Emit() {
    fmt.Printf("%v", self)
    }
    type Emitter interface {
    Emit()
    }
    接口类型的方法
    接口定义为一个方法的集合。方法包含实际的代码。
    换句话说,一个接口就是 定义,而方法就是实现。
    因此,接收者不能定义为接口类型,
    这样做的话会引 起 invalid receiver type ... 的编译器错误。
    注:接收者类型必须是 T 或 *T,这里的 T 是类型名。
    T 叫做接收者基础类型或简称基础类型。
    基础类型一定不能使指针或接口类型,并且定义在与方法相同的包中。
     
    接口指针
    在 Go 中创建指向接口的指针是无意义的。
    实际上创建接口值的指针 也是非法的。
     
     
    4、接口名字
     
    根据规则,单方法接口命名为方法名加上 -er 后缀:Reader,Writer,Formatter 等。
    有一堆这样的命名,高效的反映了它们职责和包含的函数名。
    Read,Write, Close,Flush,String 等等有着规范的声明和含义。
    为了避免混淆,除非有类似 的声明和含义,否则不要让方法与这些重名。
    相反的,如果类型实现了与众所 周知的类型相同的方法,
    那么就用相同的名字和声明;将字符串转换方法命名 为 String 而不是 ToString。
     
    5、自省和反射
    了解一下定义在 Person 的定义中的 “标签”(这里命名为 “namestr”)。
    为了做到这个,需要 reflect 包(在 Go 中没有其他方法)。
    要记得, 查看标签意味着返回类型的定义。
    因此使用 reflect 包来指出变量的类型, 然后 访问标签。
  • 相关阅读:
    NOIP 2016 提高组 复赛 Day2T1==洛谷2822 组合数问题
    Codevs 1710 == POJ 1190 生日蛋糕 == 洛谷P1731
    [网络流24题] COGS 750 栅格网络流
    [网络流24题] COGS 运输问题1
    狂K 线段树
    Graph coloring技能树
    智能体大赛酱油记
    graph coloring学习记录
    湖北省赛酱油记
    CCCC酱油记
  • 原文地址:https://www.cnblogs.com/wjq310/p/6545511.html
Copyright © 2011-2022 走看看