接上一篇文章,继续学习接口
指针接受者与值接受者实现接口
同样可以使用指针接受者(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())
}
解释代码:
- 分别声明了两个接口:SalaryCalculator 和 LeaveCalculator。
- 定义了结构体 Employee
- 构体 Employee 实现了 SalaryCalculator 接口的 DisplaySalary 方法,接着又实现了 LeaveCalculator 接口里 CalculateLeavesLeft 方法。于是 Employee 就实现了 SalaryCalculator 和 LeaveCalculator 两个接口。
- e 赋值给了 SalaryCalculator 类型的接口变量
- e 赋值给 LeaveCalculator 类型的接口变量
- 由于 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())
}
解释代码:
- 创建了一个新的接口 EmployeeOperations,它嵌套了两个接口:SalaryCalculator 和 LeaveCalculator。
- 如果一个类型定义了 SalaryCalculator 和 LeaveCalculator 接口里包含的方法,我们就称该类型实现了 EmployeeOperations 接口。
- 由于 Employee 结构体定义了 DisplaySalary 和 CalculateLeavesLeft 方法,因此它实现了接口 EmployeeOperations。
- 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()
}
如果文章对您有帮助就点个赞~~~·,可以关注作者持续更新。