Go 語言介面及使用介面實現連結串列插入

隱姓埋名4869發表於2022-04-19

@


1. 介面定義

  • Interface 型別可以定義一組方法,不需要實現,並且不能包含任何的變數,稱之為介面

  • 介面不需要顯示的實現,只需要一個變數,含有介面型別中的所有方法,那麼這個變數就實現了這個介面,如果一個變數含有多個interface 型別的方法,那麼這個變數就實現了多個介面

  • 介面又稱為動態資料型別,在進行介面使用的的時候,會將介面對位置的動態型別改為所指向的型別
    會將動態值改成所指向型別的結構體

  • 每個介面由數個方法組成,介面的定義格式如下:
    其中引數列表和返回值列表中的引數變數名可以省略

type 介面型別名 interface{
    方法名1( 引數列表1 ) 返回值列表1
    方法名2( 引數列表2 ) 返回值列表2
    …
}
  • 自定義介面步驟
    ① 定義介面
    ② 定義結構體
    ③ 介面實現(繫結結構體)
    ④ 定義介面變數,初始化結構體,呼叫介面實現功能

1.1 空介面

空介面就相當於一個空指標

package main

import "fmt"

//定義空介面
type Test interface{}

func main() {
	//宣告介面方法1
	var t Test
	fmt.Printf("t的型別: %T, t的值: %v\n", t, t)
	//宣告介面方法2
	var a interface{}
	var b int
	a = b
	fmt.Printf("a的型別: %T, a的值: %v\n", a, a)
}

//輸出結果如下
t的型別: <nil>, t的值: <nil>
a的型別: int, a的值: 0

1.2 實現單一介面

結構體使用介面列印資訊

package main

import "fmt"

type Student struct {
	Name  string
	Age   int
	Score float32
}

//介面定義:介面是功能的抽象,不需要實現
type Test interface {
	Print()
}

//指標型別實現介面
func (p *Student) Print() {
	fmt.Printf("name:[%s]\n", p.Name)
	fmt.Printf("age:[%d]\n", p.Age)
	fmt.Printf("score:[%f]\n", p.Score)
}

//值型別實現介面
/*
func (p Student) Print() {
	fmt.Printf("name:[%s]\n", p.Name)
	fmt.Printf("age:[%d]\n", p.Age)
	fmt.Printf("score:[%f]\n", p.Score)
}
*/

func main() {
	//宣告介面變數
	var t Test
	//結構體初始化
	var stu Student = Student{
		Name:  "zhangsan",
		Age:   18,
		Score: 90,
	}
	//把結構體賦值給介面
	t = &stu
	//介面功能
	t.Print()
}


//輸出結果如下
name:[zhangsan]
name:[18]
name:[90.000000]

1.3 介面多方法實現

package main

import "fmt"

type Student struct {
	Name  string
	Age   int
	Score float32
}

//介面定義:介面是功能的抽象,不需要實現
type Test interface {
	Print()
	Sleep()
}

//介面的實現
func (p *Student) Print() {
	fmt.Printf("name:[%s]\n", p.Name)
	fmt.Printf("age:[%d]\n", p.Age)
	fmt.Printf("score:[%f]\n", p.Score)

}

//介面中包含多個方法,如果要使用此介面就要實現介面中包含的所有方法
func (p *Student) Sleep() {
	fmt.Println("正在睡眠~")
}

func main() {
	//宣告介面變數
	var t Test
	//結構體初始化
	var stu Student = Student{
		Name:  "zhangsan",
		Age:   18,
		Score: 90,
	}
	//把結構體賦值給介面
	t = &stu
	//介面功能
	t.Print()
	t.Sleep()
}


//輸出結果如下
name:[zhangsan]
name:[18]
name:[90.000000]
正在睡眠~
  • 示例,在電腦上定義一個USB介面,實現滑鼠、U盤、風扇的功能
package main

import "fmt"

//定義電腦
type Computer struct {
	Brand string	//品牌
	Price float32	//價格
}

//定義USB介面
type USB interface {
	mouse()
	store()
	fan()
}

//介面功能實現
func (c Computer) mouse() {
	fmt.Println("滑鼠")
}

func (c Computer) store() {
	fmt.Println("U盤")
}

func (c Computer) fan() {
	fmt.Println("風扇")
}

func main() {
	//初始化結構體
	var com Computer
	//初始化介面
	var usb USB
	com.Brand = "thinkpad"
	com.Price = 5000
	//介面呼叫
	usb = com
	usb.mouse()
	usb.fan()
	usb.store()
}


//輸出結果如下
滑鼠
風扇
U盤

2. 多型

  • 對於同一個介面,賦予給不同的結構體,使用相同的方法而產生出不同的操作,稱之為多型。

2.1 為不同資料型別的實體提供統一的介面

package main

import "fmt"

//父結構體
type Persion struct {
	Name string
	Age  int
}

//學生子結構體
type Student struct {
	Persion
	Score float32
}

//教師子結構體
type Teacher struct {
	Persion
	Class int
}

//介面定義:介面時功能的抽象,不需要實現
type Test interface {
	Print()
	Sleep()
}

//學生結構體的實現
func (p *Student) Print() {
	fmt.Printf("name:[%s]\n", p.Name)
	fmt.Printf("age:[%d]\n", p.Age)
	fmt.Printf("Score:[%f]\n", p.Score)
}

//教師結構體的實現
func (p *Teacher) Print() {
	fmt.Printf("name:[%s]\n", p.Name)
	fmt.Printf("age:[%d]\n", p.Age)
	fmt.Printf("Class:[%d]\n", p.Class)

}

//介面中包含多個方法,如果要使用此介面就要實現介面中包含的所有方法
func (p *Student) Sleep() {
	fmt.Println("正在睡眠~")
}

func (p *Teacher) Sleep() {
	fmt.Println("正在休息~")
}

func main() {
	//宣告介面變數
	var t Test
	//學生初始化
	var stu Student
	stu.Name = "zhangsan"
	stu.Age = 18
	stu.Score = 90

	//教師初始化
	var tea Teacher
	tea.Name = "lisi"
	tea.Age = 25
	tea.Class = 3

	//學生介面功能呼叫實現
	t = &stu
	t.Print()
	t.Sleep()
	fmt.Println("----------------------------")
	//教師介面功能呼叫實現
	t = &tea
	t.Print()
	t.Sleep()
}


//輸出結果如下
name:[zhangsan]
age:[18]
Score:[90.000000]
正在睡眠~
----------------------------
name:[lisi]
age:[25]
Class:[3]
正在休息~

2.2 多介面的實現

package main

import "fmt"

//介面1
type Test1 interface {
	Print()
}

//介面2
type Test2 interface {
	Sleep()
}

//結構體
type Student struct {
	Name  string
	Age   int
	Score float32
}

//介面1實現
func (s Student) Print() {
	fmt.Printf("name:[%s]\n", s.Name)
}

//介面2實現
func (s Student) Sleep() {
	fmt.Println("正在睡眠")
}

func main() {
	//介面1變數
	var t1 Test1
	//介面2變數
	var t2 Test2
	//初始化結構體
	var stu Student = Student{
		Name:  "zhangsan",
		Age:   18,
		Score: 90,
	}
	//呼叫介面實現功能
	t1 = stu
	t1.Print()

	t2 = stu
	t2.Sleep()
}


//輸出結果如下
name:[zhangsan]
正在睡眠

3. 系統介面呼叫

  • 示例
    使用介面進行排序
package main

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

//結構體
type Student struct {
    Name  string
    Age   int
    Score float32
}

//切片
type StudentArray []Student

//go語言提供了sort 介面。使用介面裡的方法即可
//實現sort介面
func (sa StudentArray) Len() int {
    return len(sa)
} //獲取切片長度
func (sa StudentArray) Less(i, j int) bool {
    return sa[i].Name > sa[j].Name
} //兩數比大小
func (sa StudentArray) Swap(i, j int) {
    sa[i], sa[j] = sa[j], sa[i]
} //兩數交換

func main() {
    //Student 切片
    var stus StudentArray

    //生成10個結構體,放入切片中
    for i := 0; i < 10; i++ {
        var stu Student = Student{
            Name:  fmt.Sprintf("stu%d", rand.Intn(100)),
            Age:   rand.Intn(120),
            Score: rand.Float32() * 100,
        }

        //結構體元素存入到切片中
        stus = append(stus, stu)
    }

    //遍歷
    for _, v := range stus {
        fmt.Println(v)
    }

    fmt.Println("--------------------------")
    //排序
    sort.Sort(stus)
    //遍歷
    for _, v := range stus {
        fmt.Println(v)
    }
}

4. 介面巢狀

  • 示例:
    檔案讀寫測試
package main

import "fmt"

//讀取的介面
type Reader interface {
	Read()
}

//寫入的介面
type Writer interface {
	Writer()
}

//介面的巢狀
type ReadWriter interface {
	Reader
	Writer
}

//檔案結構體
type File struct{}

//實現Reader介面
func (f *File) Read() {
	fmt.Println("檔案讀取")
}

//實現Writer介面
func (f *File) Writer() {
	fmt.Println("檔案寫入")
}

//定義讀寫操作函式
func Test(rw ReadWriter) {  //rw為介面變數
	rw.Read()				//使用讀寫的方法
	rw.Writer()
}

func main() {
	var f File				//定義結構體,初始化檔案
	Test(&f)
}


//輸出結果如下
檔案讀取
檔案寫入

5. 型別斷言

  • 作用:因為介面是一般型別,需要明確具體型別的時候就需要使用型別斷言

示例

package main

import "fmt"

func main() {
	//定義空介面
	var a interface{}
	var b int
	a = b //a為int型別
	//斷言賦值
	fmt.Printf("a= %v, 型別: %T\n", a, a)
	c := a.(int)
	fmt.Printf("c= %v, 型別: %T\n", c, c)
}


//輸出結果如下
a= 0, 型別: int
c= 0, 型別: int

5.1 斷言判斷

package main

import "fmt"

func main() {
	//定義空介面
	var a interface{}
	var b string
	a = b //a為int型別
	//斷言賦值
	fmt.Printf("a= %v, 型別: %T\n", a, a)
	c, err := a.(int)
	if err {
		fmt.Printf("c= %v, 型別: %T\n", c, c)
	} else {
		fmt.Println("不是int型別")
	}
}

//輸出結果如下
a= , 型別: string
不是int型別
package main

import "fmt"

func Test(t interface{}) {
	//轉換型別判斷
	v, err := t.(int)
	if !err {
		fmt.Println("type is not int")
		return
	}
	v++
	fmt.Println(v)
}

func main() {
	a := "張三"
	Test(a)
}

//輸出結果如下
type is not int

5.2 多型別判斷

package main

import "fmt"

func classifier(items ...interface{}) {
	//遍歷複雜集合
	for i, v := range items {
		//變數.(type)職能作用在switch語句中,專門用於判斷型別
		switch v.(type) {
		case bool:
			fmt.Printf("第 %d 個資料型別是 bool\n", i)
		case int, int32, int64:
			fmt.Printf("第 %d 個資料型別是 int\n", i)
		case float32, float64:
			fmt.Printf("第 %d 個資料型別是 float\n", i)
		case string:
			fmt.Printf("第 %d 個資料型別是 string\n", i)
		default:
			fmt.Printf("第 %d 個資料型別是其他型別\n", i)
		}
	}
}

func main() {
	//傳入多種型別引數
	classifier("張三", 3.14, true, 80, nil)
}


//輸出結果如下
第 0 個資料型別是 string
第 1 個資料型別是 float
第 2 個資料型別是 bool
第 3 個資料型別是 int
第 4 個資料型別是其他型別

6. 使用介面實現連結串列插入

package main

import "fmt"

//節點結構體
type LinkNode struct {
    data interface{}
    next *LinkNode
}

//連結串列結構體
type Link struct {
    head *LinkNode
    tail *LinkNode
}

//從頭部插入
func (p *Link) InsertHead(data interface{}) {
    node := &LinkNode{
        data: data,
        next: nil,
    }
    //判斷是否為空連結串列
    if p.head == nil && p.tail == nil {
        p.head = node
        p.tail = node
        return
    }
    //當前節點的next是原頭部節點
    node.next = p.head
    //更新頭部
    p.head = node
}

//從尾部插入
func (p *Link) InsertTail(data interface{}) {
    node := &LinkNode{
        data: data,
        next: nil,
    }

    //判斷是否為空連結串列
    if p.head == nil && p.tail == nil {
        p.head = node
        p.tail = node
        return
    }

    //原尾部節點的next是當前節點
    p.tail.next = node
    //更新尾部
    p.tail = node
}

//遍歷方法
func (p *Link) Req() {
    lp := p.head
    for lp != nil {
        fmt.Println(lp)
        lp = lp.next
    }
}

func main() {
    //定義連結串列
    var intLink Link
    for i := 0; i < 10; i++ {
        //intLink.InsertHead(i)
        intLink.InsertTail(i)
    }
    intLink.Req()
}


//輸出結果如下
&{0 0xc000096078}
&{1 0xc000096090}
&{2 0xc0000960a8}
&{3 0xc0000960c0}
&{4 0xc0000960d8}
&{5 0xc0000960f0}
&{6 0xc000096108}
&{7 0xc000096120}
&{8 0xc000096138}
&{9 <nil>}

相關文章