(7)Go語言學習筆記 - 接口(interface)

1 接口簡單使用場景

package main

import (
	"fmt"
)
// 定義一個 Usb 接口
type Usb interface{
	Start()
	Stop()
}

// 定義手機結構體
type Phone struct{

}
// 讓Phone實現Usb接口的方法
func (p Phone) Start(){
	fmt.Println("手機正在啓動中......")
}
func (p Phone) Stop(){
	fmt.Println("手機正在關閉中......")
}

// 定義相機結構體
type Camera struct{

}
// 讓Camera實現Usb接口的方法
func (c Camera) Start(){
	fmt.Println("相機正在啓動中......")
}
func (c Camera) Stop(){
	fmt.Println("相機正在關閉中......")
}

// 定義一個Computer結構體
type Computer struct{

}
// 編寫一個Working方法,接收一個Usb接口類型變量
// 只要是實現了Usb接口(所謂實現Usb接口,就是指實現了Usb接口聲明的所有方法)
func (c Computer) Working(usb Usb) { // usb變量會根據傳入的實參,來判斷到底是Phone還是Camera

	// 通過usb接口變量來調用Start和Stop方法
	usb.Start()
	usb.Stop()

}

func main()  {

	// 測試,先創建結構體變量
	phone := Phone{}
	camera := Camera{}
	computer := Computer{}
	computer.Working(phone)
	//手機正在啓動中......
	//手機正在關閉中......
	computer.Working(camera)
	//相機正在啓動中......
	//相機正在關閉中......

}

2 什麼是接口

interface類型可以定義一組方法,但是這些不需要實現。並且interface不能包含任何變量。到某個自定義類型(比如結構體Phone)要使用的時候,在根據具體情況把這些方法寫出來(實現)。

基本語法:

	// 定義接口類型
	type 接口名 interface{
		method1(參數列表) 返回值列表
		method2(參數列表) 返回值列表
		...
	}
	
	// 實現接口所有方法
	func (t 自定義類型) method1(參數列表) 返回值列表 {
		// 方法實現
	}
	func (t 自定義類型) method2(參數列表) 返回值列表 {
		// 方法實現
	}
	...

說明:

  • (1)接口裏的所有方法都沒有方法體,即接口的方法都是沒有實現的方法。接口體現了程序設計的多態高內聚低耦合的思想。
  • (2)Golang中的接口,不需要顯示的實現。只需要一個變量,含有接口類型中的所有方法,那麼這個變量就實現這個接口。因此,Golang中沒有implement這樣的關鍵字。

2.1 details

  • (1)接口本身不能創建實例,但是可以指向一個實現了該接口的自定義類型的變量(實例)
package main

import (
	"fmt"
)

type AInterface interface {
	Say()
}
type Student struct {
	Name string
}

func (stu Student) Say() {
	fmt.Println("stu Say()")
}

func main() {

	var stu Student
	var a AInterface = stu
	a.Say()  // stu Say()

}

  • (2)接口中所有的方法都沒有方法體,即都是沒有實現的方法
  • (3)在Golang中,一個自定義類型需要將某個接口的所有方法都實現,我們才說這個自定義類型實現了該接口。
  • (4)一個自定義類型只有實現了某個接口,才能將該自定義類型的實例(變量)賦值給接口類型
  • (5)只要是自定義數據類型,就可以實現接口,不僅僅是結構體類型。
type integer int

func (i integer) Say() {
	fmt.Println("integer i Say() =", i)
}

func main() {
	var i integer = 10
	var b AInterface = i
	b.Say() // integer i Say() = 10
}
  • (6)一個自定義數據類型可以實現多個接口
type AInterface interface {
	Say()
}

type BInterface interface{
	Hello()
}

type Monster struct{

}
func (m Monster) Hello(){
	fmt.Println("Monster hello")
}
func (m Monster) Say(){
	fmt.Println("Monster Say")
}

func main() {
	var a2 AInterface = Monster
	var b2 BInterface = Monster
	a2.Say() // Monster Say
	b2.Hello()  //Monster hello
}
  • (7)Golang接口中不能有常量
type AInterface interface{
	Name string  // 錯誤的寫法
	Test01()
	Test02()
}
  • (8)一個接口(比如A接口)可以繼承多個別的接口(比如B、C接口),這時如果要實現A接口,也必須將B、C接口的方法也全部實現。
package main

import (
	"fmt"
)

type BInterface interface {
	test01()
}

type CInterface interface {
	test02()
}

type AInterface interface {
	BInterface
	CInterface
	test03()
}

type Student struct {

}
func (stu Student) test01(){
	fmt.Println("stu test01()......")
}
func (stu Student) test02(){
	fmt.Println("stu test02()......")
}
func (stu Student) test03(){
	fmt.Println("stu test02()......")
}

func main()  {
	var stu Student
	var a AInterface = stu
	a.test01()  // stu test01()......
	a.test02()  // stu test02()......
	a.test03()  // stu test03()......

}

  • (9)interface類型默認是一個指針(引用類型),如果沒有對interface初始化就使用,那麼會輸出nil。
  • (10)空接口interface{}沒有任何方法,即所有類型都實現了空接口,我麼可以把任何一個變量賦給空接口。

type T interface{

}

type Student struct {
	Name string
}

func (stu Student) Say() {
	fmt.Println("stu Say()")
}

func main() {

	var stu Student
	var t T = stu
	fmt.Println(t)
	var t2 interface{} = stu
	var num1 float64 = 8.8
	t2 = num1
	t = num1
	fmt.Println(t2, t)

}

2.2 經典調用接口的實例

實現對結構體切片的排序 sort.Sort(data Interface)

package main

import (
	"fmt"
	"sort"
	"math/rand"
)

// (1)聲明一個Hero結構體
type Hero struct {
	Name string
	Age int
}

// (2)聲明一個Hero結構體的切片類型
type HeroSlice []Hero

// (3)實現Interface接口
func (hs HeroSlice) Len() int {
	return len(hs)
}

// Less方法就是決定使用什麼標準進行排序
func (hs HeroSlice) Less(i, j int) bool {
	// 如果i 排在j前面,那麼返回true
	return hs[i].Age < hs[j].Age
}

func (hs HeroSlice) Swap(i, j int) {
	hs[i], hs[j] = hs[j], hs[i]
}

func main()  {

	var intSlice = []int{13, 1, -1, 0, 15, 56}

	//對切片進行排序
	sort.Ints(intSlice)
	fmt.Println(intSlice)  // [-1 0 1 13 15 56]

	//對結構體切片進行排序
	var heroes HeroSlice
	for i := 0; i < 10; i++{
		hero := Hero{
			Name : fmt.Sprintf("英雄~%d", rand.Intn(100)),
			Age : rand.Intn(100),
		}
		//	將hero append到heroes切片
		heroes = append(heroes, hero)
	}

	// 排序前 遍歷切片
	for _, v := range(heroes){
		fmt.Println("排序前", v)
	}

	// 調用sort.Sort
	sort.Sort(heroes)
	for _, v := range(heroes){
		fmt.Println("排序後", v)
	}
}

2.3 接口和繼承的關係

  • (1)接口和繼承解決的問題不同
    • 繼承的價值主要在於:解決代碼的複用性和可維護性。
    • 接口的價值主要在於:設計,設計好各種規範(方法),讓其它自定義類型去實現這些方法
  • (2)接口比繼承更加靈活
    • 繼承是滿足 is - a的關係,而接口只需要滿足 like - a的關係即可
  • (3)接口在一定程度上實現代碼解耦
package main

import "fmt"

type BirdFly interface {
	Flying()
}

// 定義一個Monkey猴子結構體
type Monkey struct {
	Name string
}

func (m *Monkey) Climbing() {
	fmt.Println(m.Name, "天生會爬樹...")
}

type LittleMonkey struct {
	Monkey // 繼承
}

func (m *LittleMonkey) Flying() {
	fmt.Println(m.Name, "通過後天學習,會飛翔了....")
}


func main()  {

	monkey := LittleMonkey{
		Monkey{
			Name : "孫悟空",
		},
	}
	monkey.Climbing()  // 孫悟空 天生會爬樹...
	monkey.Flying()

}
  • (1)A結構體繼承了B結構體,那麼A結構體就自動的繼承了B結構體的字段和方法,並且可以直接使用
  • (2)當A結構體需要擴展功能,同時又不希望去破壞繼承關係,則可以去實現某個接口即可,因此可以認爲:實現接口是對繼承機制的補充。

3 多態

變量(實例)具有多種形態。面向對象的第三大特徵,在Go語言中,多態特徵是通過接口實現的。可以按照統一的接口來調用不同的實現。這時接口變量就呈現不同的形態。
在下邊的Usb 的案例就是多態的體現,usb Usb,既可以接收手機變量,又可以接收相機變量,就體現了Usb接口多態特性

  • (1)多態參數
  • (2)多態數組
package main

import (
	"fmt"
)
// 定義一個 Usb 接口
type Usb interface{
	Start()
	Stop()
}

// 定義手機結構體
type Phone struct{
	Name string
}
// 讓Phone實現Usb接口的方法
func (p Phone) Start(){
	fmt.Println("手機正在啓動中......")
}
func (p Phone) Stop(){
	fmt.Println("手機正在關閉中......")
}

// 定義相機結構體
type Camera struct{
	Name string
}
// 讓Camera實現Usb接口的方法
func (c Camera) Start(){
	fmt.Println("相機正在啓動中......")
}
func (c Camera) Stop(){
	fmt.Println("相機正在關閉中......")
}

func main()  {
	// 定義一個Usb接口數組,可以存放Phone和Camera的結構體變量
	// 這裏就體現出多態數組
	var usbArr [3]Usb
	usbArr[0] = Phone{"apple"}
	usbArr[1] = Phone{"華爲"}
	usbArr[2] = Camera{"Nikon"}

	fmt.Println(usbArr)  // [{apple} {華爲} {Nikon}]

}

4 斷言

斷言引出:如何將一個接口變量類型,賦值給自定義類型的變量

package main

import "fmt"

type Point struct {
	x int
	y int
}

func main()  {

	var a interface{}
	var point Point = Point{1, 2}
	a = point

	var b Point
	// 此時如果想讓a賦值給b,不能直接  b = a
	// 要用類型斷言
	b = a.(Point)
	// 表示判斷a是否是指向Point類型的變量,如果是就轉成Point類型並賦給b變量,否則就報錯
	fmt.Println(b)  // 輸出 {1, 2}

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章