面向對象語言中,介面用於定義對象的行為。介面只指定對象應該做什麼,實現這種行為的方法(實現細節)是由對象來決定。 ...
一、概念
1、 面向對象語言中,介面用於定義對象的行為。介面只指定對象應該做什麼,實現這種行為的方法(實現細節)是由對象來決定。
2、 在Go語言中,介面是一組方法簽名。
- •介面只指定了類型應該具有的方法,類型決定瞭如何實現這些方法。
- •當某個類型為介面中的所有方法提供了具體的實現細節時,這個類型就被稱為實現了該介面。
- •介面定義了一組方法,如果某個對象實現了該介面的所有方法,則此對象就實現了該介面。
3、 Go語言的類型都是隱式實現介面的。任何定義了介面中所有方法的類型都被稱為隱式地實現了該介面。
二、介面的使用
go沒有 implements, extends 關鍵字,其實這種編程語言叫做duck typing編程語言。
package main import "fmt" import "base" //定義介面 type Phone interface { call() } type AndroidPhone struct { } type IPhone struct { } func (a AndroidPhone) call() { fmt.Println("我是安卓手機,可以打電話了") } func (i IPhone) call() { fmt.Println("我是蘋果手機,可以打電話了") } func main() { // 定義介面類型的變數 var phone Phone //phone = new(AndroidPhone) phone = AndroidPhone{} fmt.Printf("%T , %v , %p \n" , phone , phone , &phone) phone.call() //phone = new(IPhone) phone = IPhone{} fmt.Printf("%T , %v , %p \n" , phone , phone , &phone) phone.call() }View Code
動態類型與靜態類型語言
- •動態類型的好處很多,Python代碼寫起來很快。但是缺陷也是顯而易見的:錯誤往往要在運行時才能被髮現。
- •相反,靜態類型語言往往在編譯時就是發現這類錯誤:如果某個變數的類型沒有顯式聲明實現了某個介面,那麼,這個變數就不能用在要求一個實現了這個介面的地方。
Go類型系統採取了折中的辦法:
•之所以說這是一種折中的辦法,原因如下:
- 〇第一,結構體類型T不需要顯式地聲明它實現了介面 I。只要類型T實現了介面1規定的所有方法,它就自動地實現了介面 I。這樣就像動態語言一樣省了很多代碼,少了許多限制。
- 〇第二,將結構體類型的變數顯式或者隱式地轉換為介面 I類型的變數i。這樣就可以和其它靜態類型語言一樣,在編譯時檢查參數的合法性。
三、多態
•事物的多種形態
- • Go中的多態性是在介面的幫助下實現的。定義介面類型,倉U建實現該介面的結構體對象。
- •定義介面類型的對象,可以保存實現該介面的任何類型的值。Go語言介面變數的這個特性實現了 Go語言中的多態性。
- •介面類型的對象,不能訪問其實現類中的屬性欄位。
package main import "fmt" import "base" type Income interface { calculate() float64 //計算收入總額 source() string //用來說明收入來源 } //固定賬單項目 type FixedBilling struct { projectName string //工程項目 biddedAmount float64 //項目招標總額 } //定時生產項目(定時和材料項目) type TimeAndMaterial struct { projectName string workHours float64 //工作時長 hourlyRate float64 //每小時工資率 } //固定收入項目 func (f FixedBilling) calculate() float64 { return f.biddedAmount } func (f FixedBilling) source() string { return f.projectName } //定時收入項目 func (t TimeAndMaterial) calculate() float64 { return t.workHours * t.hourlyRate } func (t TimeAndMaterial) source() string { return t.projectName } //通過廣告點擊獲得收入 type Advertisement struct { adName string clickCount int incomePerclick float64 } func (a Advertisement) calculate() float64 { return float64(a.clickCount) * a.incomePerclick } func (a Advertisement) source() string { return a.adName } func main() { p1 := FixedBilling{"項目1", 5000} p2 := FixedBilling{"項目2", 10000} p3 := TimeAndMaterial{"項目3", 100, 40} p4 := TimeAndMaterial{"項目4", 250, 20} p5 := Advertisement{"廣告1", 10000, 0.1} p6 := Advertisement{"廣告2", 20000, 0.05} ic := []Income{p1, p2, p3, p4, p5, p6} fmt.Println("total=",calculateNetIncome(ic)) } //計算凈收入 func calculateNetIncome(ic []Income) float64 { netincome := 0.0 for _, income := range ic { fmt.Printf("收入來源:%s ,收入金額:%.2f \n", income.source(), income.calculate()) netincome += income.calculate() } return netincome }View Code
四、空介面
•空介面 :該介面中沒有任何的方法。任意類型都可以實現該介面。
•空interface這樣定義:interface{},也就是包含0個method的interface。
•用空介面表示任意數據類型。類似於java中的object。
•空介面常用於以下情形:
〇 1、println的參數就是空介面
〇 2、定義一個map: key是string,value是任意數據類型
〇 3、定義一個切片,其中存儲任意類型的數據
package main import ( "fmt" ) type A interface { } type Cat struct { name string age int } type Person struct { name string sex string } func main() { var a1 A = Cat{"Mimi", 1} var a2 A = Person{"Steven", "男"} var a3 A = "Learn golang with me!" var a4 A = 100 var a5 A = 3.14 showInfo(a1) showInfo(a2) showInfo(a3) showInfo(a4) showInfo(a5) fmt.Println("------------------") //1、fmt.println參數就是空介面 fmt.Println("println的參數就是空介面,可以是任何數據類型", 100, 3.14, Cat{"旺旺", 2}) //2、定義map。value是任何數據類型 map1 := make(map[string]interface{}) map1["name"] = "Daniel" map1["age"] = 13 map1["height"] = 1.71 fmt.Println(map1) fmt.Println("------------------") // 3、定義一個切片,其中存儲任意數據類型 slice1 := make([]interface{}, 0, 10) slice1 = append(slice1, a1, a2, a3, a4, a5) fmt.Println(slice1) transInterface(slice1) //var cat1 A = Cat{"MiaoMiao" , 3} //fmt.Println(cat1.name , cat1.age) } //介面對象轉型 //介面對象.(type),配合switch...case語句 func transInterface(s []interface{}) { for i := range s { fmt.Println("第", i+1 , "個數據:") switch t := s[i].(type) { case Cat: fmt.Printf("\t Cat對象,name屬性:%s,age屬性:%d \n" , t.name , t.age) case Person: fmt.Printf("\t Person對象,name屬性:%s,sex屬性:%s \n" , t.name , t.sex) case string: fmt.Println("\t string類型" , t) case int: fmt.Println("\t int類型" , t) case float64: fmt.Println("\t float64類型" , t) } } } func showInfo(a A) { fmt.Printf("%T , %v \n", a, a) }View Code
五、介面對象轉型
1、 方式一:
• instance, ok :=介面對象.(實際類型)
•如果該介面對象是對應的實際類型,那麼instance就是轉型之後對象,ok的值為true
•配合if... else if...語句使用
2、 方式二:
•介面對象.(type)
•配合switch...case語句使用
package main import "fmt" import ( "base" "math" ) //1、定義介面 type Shape interface { perimeter() float64 area() float64 } //2.矩形 type Rectangle struct { a, b float64 } //3.三角形 type Triangle struct { a, b, c float64 } //4.圓形 type Circle struct { radius float64 } //定義實現介面的方法 func (r Rectangle) perimeter() float64 { return (r.a + r.b) * 2 } func (r Rectangle) area() float64 { return r.a * r.b } func (t Triangle) perimeter() float64 { return t.a + t.b + t.c } func (t Triangle) area() float64 { //海倫公式 p := t.perimeter() / 2 //半周長 return math.Sqrt(p * (p - t.a) * (p - t.b) * (p - t.c)) } func (c Circle) perimeter() float64 { return 2 * math.Pi * c.radius } func (c Circle) area() float64 { return math.Pow(c.radius, 2) * math.Pi } //介面對象轉型方式1 //instance,ok := 介面對象.(實際類型) func getType(s Shape) { if instance, ok := s.(Rectangle); ok { fmt.Printf("矩形:長度%.2f , 寬度%.2f , ", instance.a, instance.b) } else if instance, ok := s.(Triangle); ok { fmt.Printf("三角形:三邊分別:%.2f , %.2f , %.2f , ", instance.a, instance.b, instance.c) } else if instance, ok := s.(Circle); ok { fmt.Printf("圓形:半徑%.2f , ", instance.radius) } } //介面對象轉型——方式2 //介面對象.(type), 配合switch和case語句使用 func getType2(s Shape) { switch instance := s.(type) { case Rectangle: fmt.Printf("矩形:長度為%.2f , 寬為%.2f ,\t", instance.a, instance.b) case Triangle: fmt.Printf("三角形:三邊分別為%.2f ,%.2f , %.2f ,\t", instance.a, instance.b, instance.c) case Circle: fmt.Printf("圓形:半徑為%.2f ,\t", instance.radius) } } func getResult(s Shape) { getType2(s) fmt.Printf("周長:%.2f ,面積:%.2f \n", s.perimeter(), s.area()) } func main() { var s Shape s = Rectangle{3, 4} getResult(s) showInfo(s) s = Triangle{3, 4, 5} getResult(s) showInfo(s) s = Circle{1} getResult(s) showInfo(s) x := Triangle{3, 4, 5} fmt.Println(x) } func (t Triangle) String() string { return fmt.Sprintf("Triangle對象,屬性分別為:%.2f, %.2f, %.2f", t.a, t.b, t.c) } func showInfo(s Shape) { fmt.Printf("%T ,%v \n", s, s) fmt.Println("-------------------") }View Code