Hike News
Hike News

Golang物件導向初識-day19-接口嵌套、類型斷言

接口嵌套 (接口繼承)

一個接口可以嵌套在另外的接口,如下所示

範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package main

import "fmt"

type ReadWrite interface {
Read()
Write()
}

type Lock interface {
Lock()
Unlock()
}

//File接口 包含ReadWrite及Lock兩接口
type File interface {
ReadWrite
Lock
close()
}

//函數傳參的接收類型為 File接口類型,只要自訂義的類型實現File接口的所有方法
//其自定義類型實例化的變量 就能作為參數傳進此函數並調用此函數
func File_operate(f File){
f.Write()
f.Read()
f.Lock()
f.Unlock()
f.close()
}

type Test struct {
Name string
}

//以下 Test類型 實現了File接口的所有方法
func (self Test) Read(){
fmt.Println("Read....")
}

func (self Test) Write(){
fmt.Println("Write....")
}

func (self Test) Lock(){
fmt.Println("File Lock !!")
}

func (self Test) Unlock(){
fmt.Println("File Unlock !!")
}

func (self Test) close(){
fmt.Println("File close...")
}

func main(){

//因 Test類型實現了File接口的所有方法
//所以Test類型實例化的變量test亦為File接口類型
//所以test變量可作為參數傳入File_operate函數,並調用適用此接口類型的方法或函數
var test Test
File_operate(test)
}
  • 一種接口類型可放到其他接口類型中
  • 若要實現File接口,則ReadWriteLock接口的所有方法及close()方法都必須實現
  • 類似結構體嵌套匿名結構體

result

1
2
3
4
5
Write....
Read....
File Lock !!
File Unlock !!
File close...

類型斷言 (類型轉換)

  • 由於接口是一般類型,並不知道具體類型
  • 任何類型只要滿足a接口內所有的方法,該類型亦為a接口類型
  • 再調用函數時,參數接受a接口類型時,並不知道傳參進來的a接口指向的是何種類型的實例(如上範例)
  • 要是接口指向的是非期望的類型需進行類型轉換再操作
  • 此時便需做類型斷言

如要轉成具體類型可採用以下方式進行轉換

不安全的類型斷言

1
2
3
4
var t int
var x interface{}
x = t
y = x.(int) //將x空接口強轉成int類型

範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

//a形參接受任何類型的參數(空接口可接受任何類型)
func add (a interface{}){

//將a斷言為int類型,並賦值給b
//將接口類型轉為int類型,傳進來的參數也必須為int類型才能轉換,否則panic
b := a.(int) //[1]
b += 3
fmt.Println(b)
}

func main(){
a := 100
add(a)
}

[1] 要是傳進來的參數不為int類型時,斷言為int將會panic,因此需要安全的類型斷言


安全的類型斷言

在進行斷言之前會先檢查是否能轉換

1
2
3
4
var t int
var x interface{}
x = t
y,ok =x.(int) //將x轉成int,並帶檢查

範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func add (a interface{}){
b , ok := a.(int) //如果ok返回是true則可成功類型轉換
if ok == false {
fmt.Println("assertion Error")
return
}
b += 3
fmt.Println(b)
}

func main(){
a := 1000
add(a)
}

判斷傳參的類型(利用switch斷言)

使用switch判斷傳入參數的類型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
"fmt"
)

func determine_type (a ...interface{}){
for i,x := range a{
switch x.(type){
case bool: fmt.Println("number",i,"type is bool")
case int: fmt.Println("number",i,"type is integer")
case string: fmt.Println("number",i,"type is string")
case nil: fmt.Println("number",i,"type is nil")
case int64: fmt.Println("number",i,"type is int64")
case float64,float32: fmt.Println("number",i,"type is float")
default:
fmt.Println("I don't know what's the type of",i)
}
}
}

func main(){
determine_type("a",int64(133),true,false,"abcd",'c',123456,333.3333)
}

  • 須注意x.(type)這樣的用法,用來作類型斷言的判斷非常好用
  • 也可用來判斷自定義的類型,如struct、指針類型等

result

1
2
3
4
5
6
7
8
number 0 type is string
number 1 type is int64
number 2 type is bool
number 3 type is bool
number 4 type is string
I don't know what's the type of 5
number 6 type is integer
number 7 type is float

tips

  • 類型斷言不一定是只拿某個接口類型的變量判斷是否能轉成其他類型
  • 其他類型的變量也能拿來判斷是否實現了接口 (比較少用)

practice

  • 實現一個可存放任何類型data的鏈表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    package main

    import "fmt"

    type data_save struct {
    data interface{}
    next *data_save
    }

    type record_pointer struct {
    Head *data_save
    Tail *data_save
    }

    func (self *record_pointer)InsertHead_CreateNode(data interface{}){
    Node := &data_save{}
    Node.data = data

    if self.Head == nil && self.Tail ==nil {
    self.Head = Node
    self.Tail = Node
    return
    }
    Node.next = self.Head
    self.Head = Node
    }


    func (self *record_pointer)InsertTail_CreateNode(data interface{}){
    Node := &data_save{}
    Node.data = data

    if self.Head == nil && self.Tail ==nil {
    self.Head = Node
    self.Tail = Node
    return
    }
    self.Tail.next = Node
    self.Tail = Node
    }

    func (self *record_pointer)view(){
    var p *data_save = self.Head
    for p != nil {
    fmt.Println(*p)
    p = p.next
    }
    }

    func main(){
    var a record_pointer
    for i:=0;i<10;i++{
    a.InsertTail_CreateNode(i)
    }
    a.view()
    }
  • 只要存放的data接受空接口類型(interface{}),就能存放任何類型的變量

result

1
2
3
4
5
6
7
8
9
10
{0 0xc04205a400}
{1 0xc04205a420}
{2 0xc04205a440}
{3 0xc04205a460}
{4 0xc04205a480}
{5 0xc04205a4a0}
{6 0xc04205a4c0}
{7 0xc04205a4e0}
{8 0xc04205a500}
{9 <nil>}