interface
接口定義了一組方法,但是這些方法不包含實現代碼(它們是抽象的),接口裏也不能包含變量。
接口指定了一個類型應該具有的方法,並由該類型決定如何實現這些方法。類型通過實現一個接口的所有方法來實現該接口。
通常在有兩個或以上的具體類型(struct)以相同的方式(method)進行處理時使用接口。
package main
import (
"fmt"
)
type SalaryCalculator interface {
CalculateSalary() int
}
type Permanent struct {
empId int
basicpay int
pf int
}
type Contract struct {
empId int
basicpay int
}
//salary of permanent employee is sum of basic pay and pf
//此方法表示類型Permanent實現了接口SalaryCalculator
func (p Permanent) CalculateSalary() int {
return p.basicpay + p.pf
}
//salary of contract employee is the basic pay alone
func (c Contract) CalculateSalary() int {
return c.basicpay
}
/*
total expense is calculated by iterating though the SalaryCalculator slice and summing
the salaries of the individual employees
*/
func totalExpense(s []SalaryCalculator) {
expense := 0
for _, v := range s {
//v是接口類型,但會去執行對應結構體上的方法
expense = expense + v.CalculateSalary()
}
fmt.Printf("Total Expense Per Month $%d", expense)
}
func main() {
pemp1 := Permanent{1, 1000, 10}
pemp2 := Permanent{2, 2000, 20}
cemp1 := Contract{3, 3000}
employees := []SalaryCalculator{pemp1, pemp2, cemp1}
totalExpense(employees)
}
上述例子中,對象pemp1是Permanent類型,對象cemp1是Contract類型,這兩個類型都實現了SalaryCalculator接口中的所有方法,即實現了這個接口。所以pemp1和cemp1也都屬於SalaryCalculator接口類型。
所有實現了接口的類型,都可以把它的值保存在一個接口類型的變量中。在 Go 中,我們使用接口的這種特性來實現面向對象編程中的多態。
空interface
空interface(interface{}
)不包含任何的方法,所以所有的類型都實現了空interface。一般用在需要存儲任意類型的數值的時候,如一個函數把interface{}
作爲參數,那麼它可以接收任意類型的值作爲參數。
判斷interface變量存儲的類型
interface可以存儲任意類型的數值,怎麼反向知道到底存儲了什麼類型呢?兩個方法:
-
類型斷言:
value, ok = element.(T)
value是變量的值,ok是bool類型,element是interface變量,T是類型。 -
switch實現上述方法:
for index, element := range list { switch value := element.(type) { case int: // do something case string: // do something default: // ... } }
嵌入interface
儘管 Go 語言沒有提供繼承機制,但可以通過嵌套其他的接口,創建一個新接口。
類似struct的匿名字段,一個interface1也可以作爲另一個interface2的嵌入字段,此時,interface2就包含了interface1的方法。
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
。
所以可以把Employee
類型的e
賦值給EmployeeOperations
類型的empOp
,empOp
就可以調用 DisplaySalary()
和 CalculateLeavesLeft()
方法了。