Go 學習筆記 11 | Golang 接口詳解


一、Golang 接口

Golang 中接口定義了對象的行為規范,只定義規范不實現。接口中定義的規范由具體的對象來實現。

package main

import (
	"fmt"
)

//接口是一個規范
type Usber interface {  // 最好以 er 結尾表示接口
	start()
	stop()
}

// 如果接口里有方法的話,必須要通過結構體或者通過自定義類型實現這個接口。

type Phone struct {
	Name string
}

// 手機要實現 usb 接口的話必須得實現 usb 接口中的所有方法

func (p Phone) start() {
	fmt.Println(p.Name, "啟動")
}

func (p Phone) stop() {
	fmt.Println(p.Name, "關機")
}

func main() {
	p := Phone{
		Name: "華為手機",
	}
	p.start()

	var p1 Usber  // Golang 中接口就是一個數據類型
	p1 = p  // 表示手機實現 Usb 接口

	p1.start()
	p1.stop()
}

輸出:

華為手機 啟動
華為手機 啟動
華為手機 關機

空接口

空接口表示沒有任何約束,因此任何類型變量都可以實現空接口。

package main

import (
	"fmt"
)

// Golang 中空接口也可以直接當作類型來使用,可以表示任意類型
func main() {
	var a interface{}  // 空接口可以接收任意類型
	a = 20
	fmt.Printf("值: %v 類型:%T\n", a, a)
	a = "你好golang"
	fmt.Printf("值: %v 類型:%T\n", a, a)
	a = true
	fmt.Printf("值: %v 類型:%T\n", a, a)	
}

輸出:

值: 20 類型:int
值: 你好golang 類型:string
值: true 類型:bool

1. 空接口可以作為函數的參數

package main

import (
	"fmt"
)

// 1、空接口可以作為函數的參數
func show(a interface{}) {
	fmt.Printf("值:%v 類型:%T\n", a, a)
}

// Golang 中空接口也可以直接當作類型來使用,可以表示任意類型
func main() {	
	show(20)
	show("你好golang")
	slice := []int{1, 2, 3, 4}
	show(slice)
}

輸出:

值:20 類型:int
值:你好golang 類型:string
值:[1 2 3 4] 類型:[]int

2. map 的值實現空接口

package main

import (
	"fmt"
)

// 2、map 的值實現空接口
func show(a interface{}) {
	fmt.Printf("值:%v 類型:%T\n", a, a)
}

func main() {
	var m1 = make(map[string]interface{})

	m1["username"] = "張三"
	m1["age"] = 20
	m1["married"] = true
	
	fmt.Println(m1)

	var s1 = []interface{}{1, 2, "你好", true}
	fmt.Println(s1)
}

輸出:

map[age:20 married:true username:張三]
[1 2 你好 true]

類型斷言

package main

import (
	"fmt"
)

func main() {
	var a interface{}
	a = "你好golang"
	v, ok := a.(string)
	if ok {
		fmt.Println("a就是一個string類型,值是:", v)
	} else {
		fmt.Println("斷言失敗")
	}
}

輸出:

a就是一個string類型,值是: 你好golang

另一種寫法:

package main

import (
	"fmt"
)

// 1、X.(T) 括號里表示 X 可能是的類型
func MyPrint1(x interface{}) {
	if _, ok := x.(string); ok {
		fmt.Println("string類型")
	} else if _, ok := x.(int); ok {
		fmt.Println("int類型")
	} else if _, ok := x.(bool); ok {
		fmt.Println("bool類型")
	}
}

// 2、類型.(type)只能結合 switch 語句使用
func MyPrint2(x interface{}) {
	switch x.(type) {
	case int:
		fmt.Println("int類型")
	case string:
		fmt.Println("string類型")
	case bool:
		fmt.Println("bool類型")
	default:
		fmt.Println("傳入錯誤...")
	}
}

func main() {
	MyPrint1("你好golang")
	MyPrint1(true)
	MyPrint1(20)

	
	MyPrint2(true)
	MyPrint2(20)
	MyPrint2("你好golang")
}

輸出:

string類型
bool類型
int類型
bool類型
int類型
string類型

二、結構體值接收者實現接口

值接收者:如果結構體中的方法是值接收者,那么實例化后的結構體值類型和結構體指針類型都可以賦值給接口類型變量。

package main

import (
	"fmt"
)

// 接口是一個規范
type Usber interface {  // 最好以 er 結尾表示接口
	start()
	stop()
}

// 如果接口里有方法的話,必須要通過結構體或者通過自定義類型實現這個接口。

type Phone struct {
	Name string
}

// 手機要實現 usb 接口的話必須得實現 usb 接口中的所有方法

func (p Phone) start() {
	fmt.Println(p.Name, "啟動")
}

func (p Phone) stop() {
	fmt.Println(p.Name, "關機")
}

func main() {
	// 結構體值接收者實例化后的結構體值類型和結構體指針類型都可以賦值給接口變量
	var p1 = Phone{
		Name: "小米手機",
	}

	var p2 Usber = p1  // 表示讓 Phone 實現 Usb 的接口
	p2.start()

	var p3 = &Phone{
		Name: "蘋果手機",
	}

	var p4 Usber = p3
	p4.start()
}

輸出:

小米手機 啟動
蘋果手機 啟動

指針類型

package main

import (
	"fmt"
)

// 接口是一個規范
type Usber interface {  // 最好以 er 結尾表示接口
	start()
	stop()
}

// 如果接口里有方法的話,必須要通過結構體或者通過自定義類型實現這個接口。

type Phone struct {
	Name string
}

// 手機要實現 usb 接口的話必須得實現 usb 接口中的所有方法

func (p *Phone) start() {  // 指針接收者
	fmt.Println(p.Name, "啟動")
}

func (p *Phone) stop() {
	fmt.Println(p.Name, "關機")
}


func main() {
	/*
		// 錯誤寫法
		var phone1 = Phone{
			Name: "小米手機",
		}

		var p1 Usber = phone1  // 表示讓 Phone 實現 Usb 的接口
		p1.start()
	*/

	var phone1 = &Phone{
		Name: "小米",
	}
	var p1 Usber = phone1
	p1.start()
}

輸出:

小米 啟動

結構體值接收者和指針接收者實現接口的區別

值接收者:如果結構體中的方法是值接收者,那么實例化后結構體值類型結構體指針類型都可以賦值給接口變量。

指針接收者:如果結構體中的方法是指針接收者,那么實例化后結構體指針類型都可以賦值給接口變量,結構體值類型沒法賦值給接口變量。

package main

import (
	"fmt"
)

type Animaler interface {
	SetName(string)
	GetName() string
}

type Dog struct {
	Name string
}

func (d *Dog) SetName(name string) {
	d.Name = name
}

func (d Dog) GetName() string {
	return d.Name
}

type Cat struct {
	Name string
}

func (c *Cat) SetName(name string) {
	c.Name = name
}

func (c Cat) GetName() string {
	return c.Name
}

func main() {
	// Dog 實現 Animal 的接口
	d := &Dog{
		Name: "小黑",
	}
	var d1 Animaler = d
	fmt.Println(d1.GetName())
	d1.SetName("小黃")
	fmt.Println(d1.GetName())

	// Cat 實現 Animal 的接口
	c := &Cat{
		Name: "小花",
	}
	var c1 Animaler = c
	fmt.Println(c1.GetName())
}

輸出:

小黑
小黃
小花

接口嵌套

package main

import (
	"fmt"
)

type Ainterface interface {
	SetName(string)
}

type Binterface interface {
	GetName() string
}

type Animaler interface {  // 接口的嵌套
	Ainterface
	Binterface
}

type Dog struct {
	Name string
}

func (d *Dog) SetName(name string) {
	d.Name = name
}

func (d Dog) GetName() string {
	return d.Name
}

func main() {
	d := &Dog{
		Name: "小黑",
	}
	var d1 Animaler = d
	d1.SetName("小花")
	fmt.Println(d1.GetName())
}

輸出:

小花

三、Golang 中空接口和類型斷言使用細節

package main

import (
	"fmt"
)

type Address struct {
	Name string
	Phone int
}

// Golang中空接口和類型斷言使用細節
func main() {
	var userinfo = make(map[string]interface{})
	userinfo["username"] = "張三"
	userinfo["age"] = 20
	userinfo["hobby"] = []string{"睡覺", "吃飯"}

	fmt.Println(userinfo["age"])  // 20
	fmt.Println(userinfo["hobby"])  // [睡覺 吃飯]

	var address = Address {
		Name: "李四",
		Phone: 123456,
	}
	fmt.Println(address.Name)  // 李四

	userinfo["address"] = address
	fmt.Println(userinfo["address"])  // {李四 123456}

	hobby2, _ := userinfo["hobby"].([]string)  // 類型斷言

	fmt.Println(hobby2[1])  // 吃飯

	address2, _ := userinfo["address"].(Address)  // 類型斷言
	fmt.Println(address2.Name, address2.Phone)  // 李四 123456
}

輸出:

20
[睡覺 吃飯]
李四
{李四 123456}
吃飯
李四 123456

四、參考教程

Golang 教程 P37-P40


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM