29. 接口

作者: 厚土火焱 | 来源:发表于2017-08-18 19:41 被阅读125次

    在go语言中,接口类型是由一组方法定义的集合。
    一个类型是否实现了一个接口,就看这个类型是否实现了接口中定义的所有方法。在go语言中,无需特别的指明?
    定义一个接口

    type Abser interface {
        Abs() float64
    }
    

    定义一个结构体

    type Vertex struct {
        X, Y float64
    }
    

    定义两个方法,一个是结构体指针,一个是结构体。

    func (v *Vertex) Abs() float64  {
        return v.X * v.X + v.Y * v.Y
    }
    func (v Vertex) Scale() float64 {
        return v.X + v.Y
    }
    

    声明一个接口变量

    var a Abser
    

    结构体也实例化一下

    f := Vertex{3, 4}
    

    指针也是 Vertex 结构体的指针,所以可以用 f 来实例化。

    a = &f
    

    下面你可以分别看一下 a 和 f 都能实现什么方法了。

    fmt.Println(f.Abs())
    fmt.Println(f.Scale())
    fmt.Println(a.Abs())
    fmt.Println(a.Scale())
    

    仔细测试,你会发现 fmt.Println(a.Scale()) 是会报错的“a.Scale undefined (type Abser has no field or method Scale)”。是的,a 没有 Scale() 这个方法。
    为什么呢?
    因为 a 是接口 Abser,而接口中没有定义 Scale()。
    如果你加上这个定义 Scale() float64,那么,a.Scale() 就可以实现了。
    完整例子

    package main
    
    import (
        "fmt"
    )
    
    type Abser interface {
        Abs() float64
    }
    type Vertex struct {
        X, Y float64
    }
    func (v *Vertex) Abs() float64  {
        return v.X * v.X + v.Y * v.Y
    }
    func (v Vertex) Scale() float64 {
        return v.X + v.Y
    }
    func main() {
        var a Abser
        f := Vertex{3, 4}
        a = &f
    
        fmt.Println(f.Abs())
        fmt.Println(f.Scale())
        fmt.Println(a.Abs())
        //fmt.Println(a.Scale())
    }
    

    运行结果

    25
    7
    25
    

    结合上边的例子,我们可以发现,
    类型通过实现方法来实现接口,却不必要显示的声明。所以没有关键字 implements 。这是隐式接口。
    隐式接口解耦了实现接口的包和定义接口的包,实现包和定义包“互不依赖”。

    Stringers
    一个普遍存在的接口,在 fmt 中定义。

    type Stringer interface {
        String() string
    }
    

    我们给它在包内依附一个结构体,定义一个 String() 方法。

    type Cofox struct {
        name string
    }
    
    func (c *Cofox) String() string  {
        return "Joel " + c.name
    }
    

    为了区别原始的值,我们在 Strings() 内的返回值前加了一个字符串 Joel ,以作区别。

    看完整代码

    package main
    
    import (
        "fmt"
    )
    
    type Stringer interface {
        String() string
    }
    
    type Cofox struct {
        name string
    }
    
    func (c *Cofox) String() string  {
        return "Joel " + c.name
    }
    
    func main() {
        var S Stringer
        c := Cofox{"Smith"}
        S = &c
    
        fmt.Println(S.String())
        fmt.Println(c.String())
        fmt.Println(c.name)
    }
    

    运行结果如下

    Joel Smith
    Joel Smith
    Smith
    

    接口和结构体都可以使用 String() 函数方法。
    你可是试着把 String() 方法里的返回值写成

    return fmt.Sprintf("full name is Joel %v", c.name)
    

    运行自己看看结果有无不同。
    再写一个例子,这次结构体多加一个字段,看看如何应用。

    package main
    
    import "fmt"
    
    type Person struct {
        Name    string
        Age     int
    }
    
    func (p Person) String() string {
        return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
    }
    func main() {
        a := Person{"Joel", 31}
        z := Person{"Smith", 45}
        fmt.Println(a, z)
    }
    

    运行结果

    Joel (31 years) Smith (45 years)
    

    相关文章

      网友评论

        本文标题:29. 接口

        本文链接:https://www.haomeiwen.com/subject/addarxtx.html