接上一篇文章,繼續(xù)學(xué)習(xí)接口
指針接受者與值接受者實(shí)現(xiàn)接口
同樣可以使用指針接受者(Pointer Receiver)來(lái)實(shí)現(xiàn)接口。只不過(guò)在用指針接受者實(shí)現(xiàn)接口時(shí),還有一些細(xì)節(jié)需要注意。
示例:
package main
import "fmt"
type Describer interface {
Describe()
}
type Person struct {
name string
age int
}
func (p Person) Describe() { // 使用值接受者實(shí)現(xiàn)
fmt.Printf("%s is %d years old\n", p.name, p.age)
}
type Address struct {
state string
country string
}
func (a *Address) Describe() { // 使用指針接受者實(shí)現(xiàn)
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"}
/* 如果下面一行取消注釋會(huì)導(dǎo)致編譯錯(cuò)誤:
cannot use a (type Address) as type Describer
in assignment: Address does not implement
Describer (Describe method has pointer
receiver)
*/
//d2 = a
d2 = &a // 這是合法的
// 因?yàn)樵诘?22 行,Address 類型的指針實(shí)現(xiàn)了 Describer 接口
d2.Describe()
}
上邊代碼中,如果使用 d2 = a 這種,編譯器會(huì)報(bào)錯(cuò),因?yàn)?a 變量的結(jié)構(gòu)體并沒(méi)有實(shí)現(xiàn)接口,實(shí)現(xiàn)接口的是 Address 結(jié)構(gòu)體的指針(也就是地址)。
對(duì)于使用指針接受者的方法,用一個(gè)指針或者一個(gè)可取得地址的值來(lái)調(diào)用都是合法的。但接口中存儲(chǔ)的具體值(Concrete Value)并不能取到地址,因此 d2 = a 對(duì)于編譯器無(wú)法自動(dòng)獲取 a 的地址。
我們將 a 的地址 &a 賦值給了 d2 就可以成功運(yùn)行。
實(shí)現(xiàn)多個(gè)接口
類型可以實(shí)現(xiàn)多個(gè)接口。我們看看下面程序是如何做到的:
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())
}
解釋代碼:
- 分別聲明了兩個(gè)接口:SalaryCalculator 和 LeaveCalculator。
- 定義了結(jié)構(gòu)體 Employee
- 構(gòu)體 Employee 實(shí)現(xiàn)了 SalaryCalculator 接口的 DisplaySalary 方法,接著又實(shí)現(xiàn)了 LeaveCalculator 接口里 CalculateLeavesLeft 方法。于是 Employee 就實(shí)現(xiàn)了 SalaryCalculator 和 LeaveCalculator 兩個(gè)接口。
- e 賦值給了 SalaryCalculator 類型的接口變量
- e 賦值給 LeaveCalculator 類型的接口變量
- 由于 e 的類型 Employee 實(shí)現(xiàn)了 SalaryCalculator 和 LeaveCalculator 兩個(gè)接口,因此這是合法的。
接口的嵌套
盡管 Go 語(yǔ)言沒(méi)有提供繼承機(jī)制,但可以通過(guò)嵌套其他的接口,創(chuàng)建一個(gè)新接口。
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())
}
解釋代碼:
- 創(chuàng)建了一個(gè)新的接口 EmployeeOperations,它嵌套了兩個(gè)接口:SalaryCalculator 和 LeaveCalculator。
- 如果一個(gè)類型定義了 SalaryCalculator 和 LeaveCalculator 接口里包含的方法,我們就稱該類型實(shí)現(xiàn)了 EmployeeOperations 接口。
- 由于 Employee 結(jié)構(gòu)體定義了 DisplaySalary 和 CalculateLeavesLeft 方法,因此它實(shí)現(xiàn)了接口 EmployeeOperations。
- empOp 的類型是 EmployeeOperations,e 的類型是 Employee,我們把 empOp 賦值為 e。接下來(lái)的兩行,empOp 調(diào)用了 DisplaySalary() 和 CalculateLeavesLeft() 方法。
接口的零值
接口的零值是 nil。對(duì)于值為 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)
}
}
對(duì)于值為 nil 的接口,由于沒(méi)有底層值和具體類型,當(dāng)我們?cè)噲D調(diào)用它的方法時(shí),程序會(huì)產(chǎn)生 panic 異常。
package main
type Describer interface {
Describe()
}
func main() {
var d1 Describer
d1.Describe()
}
如果文章對(duì)您有幫助就點(diǎn)個(gè)贊~~~·,可以關(guān)注作者持續(xù)更新。