Go基础语法(八)

作者: kakarotto | 来源:发表于2018-11-23 23:50 被阅读0次

    接上一篇文章,继续学习接口

    指针接受者与值接受者实现接口

    同样可以使用指针接受者(Pointer Receiver)来实现接口。只不过在用指针接受者实现接口时,还有一些细节需要注意。
    示例:

    package main
    
    import "fmt"
    
    type Describer interface {  
        Describe()
    }
    type Person struct {  
        name string
        age  int
    }
    
    func (p Person) Describe() { // 使用值接受者实现  
        fmt.Printf("%s is %d years old\n", p.name, p.age)
    }
    
    type Address struct {
        state   string
        country string
    }
    
    func (a *Address) Describe() { // 使用指针接受者实现
        fmt.Printf("State %s Country %s", a.state, a.country)
    }
    
    func main() {  
        var d1 Describer
        p1 := Person{"Sam", 25}
        d1 = p1
        d1.Describe()
        p2 := Person{"James", 32}
        d1 = &p2
        d1.Describe()
    
        var d2 Describer
        a := Address{"Washington", "USA"}
    
        /* 如果下面一行取消注释会导致编译错误:
           cannot use a (type Address) as type Describer
           in assignment: Address does not implement
           Describer (Describe method has pointer
           receiver)
        */
        //d2 = a
    
        d2 = &a // 这是合法的
        // 因为在第 22 行,Address 类型的指针实现了 Describer 接口
        d2.Describe()
    
    }
    

    上边代码中,如果使用 d2 = a 这种,编译器会报错,因为 a 变量的结构体并没有实现接口,实现接口的是 Address 结构体的指针(也就是地址)。
    对于使用指针接受者的方法,用一个指针或者一个可取得地址的值来调用都是合法的。但接口中存储的具体值(Concrete Value)并不能取到地址,因此 d2 = a 对于编译器无法自动获取 a 的地址。

    我们将 a 的地址 &a 赋值给了 d2 就可以成功运行。

    实现多个接口

    类型可以实现多个接口。我们看看下面程序是如何做到的:

    package main
    
    import (  
        "fmt"
    )
    
    type SalaryCalculator interface {  
        DisplaySalary()
    }
    
    type LeaveCalculator interface {  
        CalculateLeavesLeft() int
    }
    
    type Employee struct {  
        firstName string
        lastName string
        basicPay int
        pf int
        totalLeaves int
        leavesTaken int
    }
    
    func (e Employee) DisplaySalary() {  
        fmt.Printf("%s %s has salary $%d", e.firstName, e.lastName, (e.basicPay + e.pf))
    }
    
    func (e Employee) CalculateLeavesLeft() int {  
        return e.totalLeaves - e.leavesTaken
    }
    
    func main() {  
        e := Employee {
            firstName: "Naveen",
            lastName: "Ramanathan",
            basicPay: 5000,
            pf: 200,
            totalLeaves: 30,
            leavesTaken: 5,
        }
        var s SalaryCalculator = e
        s.DisplaySalary()
        var l LeaveCalculator = e
        fmt.Println("\nLeaves left =", l.CalculateLeavesLeft())
    }
    

    解释代码:

    1. 分别声明了两个接口:SalaryCalculator 和 LeaveCalculator。
    2. 定义了结构体 Employee
    3. 构体 Employee 实现了 SalaryCalculator 接口的 DisplaySalary 方法,接着又实现了 LeaveCalculator 接口里 CalculateLeavesLeft 方法。于是 Employee 就实现了 SalaryCalculator 和 LeaveCalculator 两个接口。
    4. e 赋值给了 SalaryCalculator 类型的接口变量
    5. e 赋值给 LeaveCalculator 类型的接口变量
    6. 由于 e 的类型 Employee 实现了 SalaryCalculator 和 LeaveCalculator 两个接口,因此这是合法的。

    接口的嵌套

    尽管 Go 语言没有提供继承机制,但可以通过嵌套其他的接口,创建一个新接口。

    package main
    
    import (  
        "fmt"
    )
    
    type SalaryCalculator interface {  
        DisplaySalary()
    }
    
    type LeaveCalculator interface {  
        CalculateLeavesLeft() int
    }
    
    type EmployeeOperations interface {  
        SalaryCalculator
        LeaveCalculator
    }
    
    type Employee struct {  
        firstName string
        lastName string
        basicPay int
        pf int
        totalLeaves int
        leavesTaken int
    }
    
    func (e Employee) DisplaySalary() {  
        fmt.Printf("%s %s has salary $%d", e.firstName, e.lastName, (e.basicPay + e.pf))
    }
    
    func (e Employee) CalculateLeavesLeft() int {  
        return e.totalLeaves - e.leavesTaken
    }
    
    func main() {  
        e := Employee {
            firstName: "Naveen",
            lastName: "Ramanathan",
            basicPay: 5000,
            pf: 200,
            totalLeaves: 30,
            leavesTaken: 5,
        }
        var empOp EmployeeOperations = e
        empOp.DisplaySalary()
        fmt.Println("\nLeaves left =", empOp.CalculateLeavesLeft())
    }
    

    解释代码:

    1. 创建了一个新的接口 EmployeeOperations,它嵌套了两个接口:SalaryCalculator 和 LeaveCalculator。
    2. 如果一个类型定义了 SalaryCalculator 和 LeaveCalculator 接口里包含的方法,我们就称该类型实现了 EmployeeOperations 接口。
    3. 由于 Employee 结构体定义了 DisplaySalary 和 CalculateLeavesLeft 方法,因此它实现了接口 EmployeeOperations。
    4. empOp 的类型是 EmployeeOperations,e 的类型是 Employee,我们把 empOp 赋值为 e。接下来的两行,empOp 调用了 DisplaySalary() 和 CalculateLeavesLeft() 方法。

    接口的零值

    接口的零值是 nil。对于值为 nil 的接口,其底层值(Underlying Value)和具体类型(Concrete Type)都为 nil。

    package main
    
    import "fmt"
    
    type Describer interface {  
        Describe()
    }
    
    func main() {  
        var d1 Describer
        if d1 == nil {
            fmt.Printf("d1 is nil and has type %T value %v\n", d1, d1)
        }
    }
    

    对于值为 nil 的接口,由于没有底层值和具体类型,当我们试图调用它的方法时,程序会产生 panic 异常。

    package main
    
    type Describer interface {
        Describe()
    }
    
    func main() {  
        var d1 Describer
        d1.Describe()
    }
    

    如果文章对您有帮助就点个赞~~~·,可以关注作者持续更新。

    相关文章

      网友评论

        本文标题:Go基础语法(八)

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