Golang interface接口全面理解(二)

90design · · 2187 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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  // 不能使用值类型(引发panic)①

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

}

为什么上面d2 = a处会引发panic:

.\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 ???

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

 

 

 

 


有疑问加站长微信联系(非本文作者)

本文来自:开源中国博客

感谢作者:90design

查看原文:Golang interface接口全面理解(二)

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

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