Golang interface接口全面理解(二)

Now your life, life in the future to play you, now do not work hard, the future suck.

现在不玩命,将来命玩你,现在不努力,未来不给力。

 指针 vs 值类型实现接口

我们在第1部分中讨论的所有示例接口都是使用值receivers 实现的。也可以使用指针receivers 来实现接口。在使用指针receivers 实现接口时需要注意的细微之处。让我们了解使用下面的程序。

package main

import "fmt"

type Describer interface {
	Describe()
}
type Person struct {
	name string
	age  int
}

func (p Person) Describe() { //implemented using value receiver
	fmt.Printf("%s is %d years old\n", p.name, p.age)
}

type Address struct {
	state   string
	country string
}

func (a *Address) Describe() { //implemented using pointer receiver
	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"}

	//d2 = a  // 不能使用值类型(编译失败)①

	d2 = &a 
	d2.Describe()
    a.Describe()  // 直接使用值类型调用②

}

为什么上面d2 = a处会引发panic( 此处不是panic, 而是引发编译错误。 

cannot use a (type Address) as type Describer  in assignment: Address does not implement        Describer (Describe method has pointer receiver), 感谢码友@神州浪子的指正):

.\interface1.go:39:5: cannot use a (type Address) as type Describer in assignment:
	Address does not implement Describer (Describe method has pointer receiver)

而a.Describe() 不会引起编译失败???

原因是: 任何指针变量或者可以获取指针的变量调用指针方法都是合法的。但是存储在接口中的值是无法寻址的,因此编译器无法自动获取指针地址引发panic.

或者说: d2 = a 此行报错, 简单地说,就是传过去(赋值)的对象必须实现了接口要求的方法, a并没有实现Describe()方法,a的指针实现了Describe()方法。

实现多个接口

一个类型可以实现多个接口。让我们看看这是如何在下面的程序中完成的。

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())
}

接口的嵌套

虽然go不提供类似JAVA的继承,但可以通过嵌入其他接口来创建新的接口。

让我们看看这是如何完成的:

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())
	
	//当然更是实现了两个子接口
	var lc SalaryCalculator = e
	lc.DisplaySalary()
}

接口零值

一个接口的零值是nil , 也有其nil的类型。

package main

import "fmt"

type Describer interface {  
    Describe()
}

func main() {  
    var d1 Describer
    if d1 == nil {
        fmt.Printf("d1 is nil and 类型 %T 值%v\n", d1, d1)
    }
}

如果我们使用nil的接口调用一个方法,则程序会panic,因为nil interface既没有底层的值也没有对应的具体类型。或者说像JAVA的空指针异常!

The End!

猜你喜欢

转载自my.oschina.net/90design/blog/1633420
今日推荐