Hike News
Hike News

Golang物件導向初識-day18-接口(interface)

實現 String()方法

如果一個變量實現了String()這個方法,那麼fmt.Printf默認會調用這個變量的String()進行輸出

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"

type ClassRoom struct {
Name string
Teacher string
RoomNum int
}

func (self *ClassRoom)String()string{
result := fmt.Sprintf("%s Class %d - %s",self.Name,self.RoomNum,self.Teacher)
return result
}

func main(){
var chem *ClassRoom = &ClassRoom{}
chem.Name = "Chemistry"
chem.Teacher = "Dr.Chang"
chem.RoomNum = 101

//調用fmt.Printf方法,格式化輸出會調用String方法
fmt.Printf("%s",chem)
}

result

1
Chemistry Class 101 - Dr.Chang
  • 以上為一接口的實現

定義

  • Interface為一種類型可以定義多個方法,但是本身不需要實現,由調用接口者實現
  • Interface不能包含任何字段、變量
  • Interface類型默認就是一個指針
1
2
3
4
5
6
7
8
9
10
type 接口名 interface{
方法1(參數列表) 返回值列表
方法2(參數列表) 返回值列表
...
}

type Test interface{
Print()
Sleep()
}
  • 只要某個類型實現了Print()Sleep()這兩個方法,這個類型也同時實現了Test這個接口
  • 要是接口裡定義多個方法,某個類型也要同時實現這些多個方法,才算是實現接口
  • 不能部份實現方法(只實現一種方法),必須全部實現

空接口

  • 要是接口裡沒有任任何方法,我們稱為空接口
  • 任何類型皆實現空接口 (空接口能指向任何類型)
1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main(){
var a interface{} //聲明一個空接口
var b int

//空接口指向int類型的實例化變量b
a = b
fmt.Printf("%T",a)
}

result

1
int

接口調用

  • Goalng中的接口,不需要顯式的實現(只要自定義的類型裡,方法名、參數、返回類型有對上即可)
  • 只要某種類型實現接口裡的所有方法,就可以將實例化的接口變量指向某種類型,並使用實例化的接口調用方法

example

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
package main

import(
"fmt"
)

//定義car接口,裡面只有一個方法run()
type car interface {
run()
}

type Bike struct{
Name string
Speed int
}

//Bike實現了car接口裡的所有方法,因此亦實現了car接口
func (self *Bike) run() {
fmt.Println("Bike running")
}

type Train struct {
Name string
Speed int
}

//Train實現了car接口裡的所有方法,因此亦實現了car接口
func (self *Train)run(){
fmt.Println("Train running")
}

func main(){
//實例化一個Bike類型
var Giant Bike
Giant.Name = "Giant"
Giant.Speed = 20

//實例化一個Train類型
var Tze_Chiang Train
Tze_Chiang.Name = "Tze_Chiang"
Tze_Chiang.Speed = 2000

//實例化一個car類型的接口(car_interface為接口類型的變量)
var car_interface car

//將實例化的接口(就是一個指針)指向實例化的Bike類型
car_interface = &Giant

//調用方法
car_interface.run()

car_interface = &Tze_Chiang
car_interface.run()
}

result

1
2
Bike running
Train running

實現多接口

  • 如果一個變量含有了多個interface類型的方法,那麼這個變量就實現了多個接口

example

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
package main

import "fmt"

type Message interface{
Send()
Receive()
}

type call interface{
Call()
}

type cellphone struct {
Name string
price int
}

func (self *cellphone)Send(){
fmt.Println("SendMessage")
}

func (self *cellphone)Receive(){
fmt.Println("ReceiveMessage...")
}

func (self *cellphone)Call(){
fmt.Println("Calling....")
}

func main(){
var Nokia *cellphone = &cellphone{
Name:"Nokia",
price : 100,
}

var message Message

//Nokia實現了message的接口
message = Nokia
message.Send()
message.Receive()

var c call

//Nokia亦實現了call的接口
c = Nokia
c.Call()
}
  • Nokia變量實現了多個接口

result

1
2
3
SendMessage
ReceiveMessage...
Calling....

tips

  • struct中必須全部實踐interface的所有方法 才算是成功調用接口 (不能部分實踐)
  • 實例化的接口 可以指向多種不同的實例化類型 (多態)

排序

每種不同的類型,皆有排序的需求,因此將排序的功能做成一個接口
只要實現排序接口的各個方法就能實現排序的功能
Golang中的sort package也有這樣的接口
https://golang.org/pkg/sort/

1
2
3
4
5
6
7
8
9
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}

example

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
65
66
67
68
69
70
71
72
73
package main

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

type Interface interface{
Len() int
Less(i,j int)bool
Swap(i,j int)
}

type Student struct{
Name string
ID int
Age int
score float64
}

//接口是針對特定類型的 因此需定義一個Student切片的別名
type StudentArray []Student

//以下為實現Sort包中Interface的三個方法
func (self StudentArray)Len()int{
return len(self)
}

//按照ID由小到大排序
func (self StudentArray)Less(i,j int)bool{
return self[i].ID < self[j].ID
}

func (self StudentArray)Swap(i,j int){
self[i],self[j] = self[j],self[i]
}

func main(){
Tom := Student{
Name : "Tom",
ID : rand.Intn(10000),
Age : 18,
score : 60.0,
}

Amy := Student{
Name : "Amy",
ID : rand.Intn(10000),
Age : 17,
score : 70.0,
}

Tony := Student{
Name : "Tony",
ID : rand.Intn(10000),
Age : 16,
score : 80.0,
}

Student_list := make(StudentArray,3)
Student_list[0] = Tom
Student_list[1] = Amy
Student_list[2] = Tony

fmt.Println("Before Sort:",Student_list)

//因為StudentArray類型實現了Interface這個接口
//因此可直接傳入StudentArray類型的變量
sort.Sort(Student_list)

fmt.Println("After Sort:",Student_list)
}
  • 可依照自己的需求 調整自訂義類型的排序方法

result

1
2
Before Sort: [{Tom 8081 18 60} {Amy 7887 17 70} {Tony 1847 16 80}]
After Sort: [{Tony 1847 16 80} {Amy 7887 17 70} {Tom 8081 18 60}]

tips

  • 只要實現sort包中Interface這個接口的規範,任何自訂義類型接能調用此接口使用sort.Sort()方法排序