Go基礎語法(八)
接上一篇文章,繼續學習介面
指標接受者與值接受者實現介面
同樣可以使用指標接受者(Pointer Receiver)來實現介面。只不過在用指標接受者實現介面時,還有一些細節需要注意。
示例:
package main import "fmt" type Describer interface { Describe() } type Person struct { name string ageint } func (p Person) Describe() { // 使用值接受者實現 fmt.Printf("%s is %d years old\n", p.name, p.age) } type Address struct { statestring 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() }
如果文章對您有幫助就點個贊~~~·,可以關注作者持續更新。