Go 接口(interface)
文章转载地址:https://www.flysnow.org/2017/04/03/go-in-action-go-interface.html
1.什么是 interface?
简单的说,interface 是一组 method 签名的组合,通过 interface 定义对象的一组行为
上一篇文章中我们实现了 Student 和 Employee 都能 SayHi,现在我们进一步做扩展,Student 和 Employee
实现另一个方法 Sing,然后 Student 实现方法 BorrowMoney 而 Employee 实现 SpendSalary
这样,Student 实现了三个方法:SayHi,Sing,BorrowMoney ;而 Employee 实现了 SayHi,Sing, SpendSalary
上面这些方法的组合被称为 interface(被对象 Student 和 Employee 实现)。例如:Student 和 Employee 都实现了 interface:
SayHi,Sing,也就是这两个对象是该 interface 类型。而 Employee 没有实现这个 interface:SayHi,Sing,BorrowMoney,
是因为 Employee 没有实现 BorrowMoney 这个方法
2. interface 类型
interface 定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了这个接口
package main import "fmt" // 定义 Human 结构体 type Human struct { name string age int phone string } // 定义 Student 结构体 type Student struct { Human // 匿名字段 company string loan float32 } // 定义结构体 Employee type Employee struct { Human // 匿名字段 company string money float32 } // Human 实现 SayHi 方法 func (h *Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } // Human 实现 Sing 方法 传入 lyrics(歌词) 参数 func (h *Human) Sing(lyrics string) { fmt.Println("La la, la la la, la la la la la...", lyrics) } // Human 实现 Guzzle 方法 func (h *Human) Guzzle(beerStein string) { fmt.Println("Guzzle Guzzle Guzzle...", beerStein) } // Employee 重载 Human 的 SayHi 方法 func (e *Employee) SayHi() { fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone) } // Student 实现 BorrowMoney 方法 func (s *Student) BorrowMoney(amount float32) { s.loan += amount } // Employee 实现 SpendSalary 方法 func (e *Employee) SpendSalary(amount float32) { e.money -= amount } // 定义 interface type Men interface { SayHi() Sing(lyrics string) Guzzle(beerStein string) } type YoungChap interface { SayHi() Sing(lyrics string) BorrowMoney(amount float32) } type ElderlyGent interface { SayHi() Sing(lyrics string) SpendSalary(amount float32) } func main() { }
通过上面的代码我们可以知道,interface 可以被任意的对象实现。我们看到上面的 Men interface 被 Human、Student、Employee
实现(一个接口可以被多个对象实现)。同理,一个对象可以实现任意多个接口,例如上面的 Student 实现了 Men 和 YoungChap 两个interface
最后,任意的类型都实现了空接口(interface{})
3. interface 值
package main import "fmt" // 定义 Human 结构体 type Human struct { name string age int phone string } // 定义 Student 结构体 type Student struct { Human // 匿名字段 company string loan float32 } // 定义结构体 Employee type Employee struct { Human // 匿名字段 company string money float32 } // Human 实现 SayHi 方法 func (h Human) SayHi() { fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone) } // Human 实现 Sing 方法 传入 lyrics(歌词) 参数 func (h Human) Sing(lyrics string) { fmt.Println("La la, la la la, la la la la la...", lyrics) } // Employee 重载 Human 的 SayHi 方法 func (e Employee) SayHi() { fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name, e.company, e.phone) } // Interface Men 都被 Human、Student、Employee 实现 type Men interface { SayHi() Sing(lyrics string) } func main() { mike := Student{Human{"Mike",25,"222-222-xxx"},"MIT",0.00} paul := Student{Human{"Paul",26,"111-222-xxx"},"Harvard",100} sam := Employee{Human{"Sam",36,"444-222-xxx"},"Golang Inc.",1000} tom := Employee{Human{"Tom",37,"222-444-xxx"},"Things Ltd.",5000} // 定义 Men 类型的 i var i Men // i 能存储 Student i = mike fmt.Println("This is Mike,a Student:") i.SayHi() i.Sing("November rain") // i 也能存储 Employee i = tom fmt.Println("This is tom, an Employee:") i.SayHi() i.Sing("Born to be wild") // 定义slice Men fmt.Println("Let's use a slice of Men and see what happens") x := make([]Men,3) // 这三个都是不同类型的元素,但是他们实现了interface同一个接口 x[0],x[1],x[2] = paul,sam,mike for _,value := range x{ value.SayHi() } } -------------------------------------------------------------------------- 输出结果: This is Mike,a Student: Hi, I am Mike you can call me on 222-222-xxx La la, la la la, la la la la la... November rain This is tom, an Employee: Hi, I am Tom, I work at Things Ltd.. Call me on 222-444-xxx La la, la la la, la la la la la... Born to be wild Let's use a slice of Men and see what happens Hi, I am Paul you can call me on 111-222-xxx Hi, I am Sam, I work at Golang Inc.. Call me on 444-222-xxx Hi, I am Mike you can call me on 222-222-xxx
4.空 interface
空 interface(interface{}) 不包含任何的 method,正因为如此,所有类型都实现了空 interface。空 interface 对于描述
起不到任何作用(因为它不包含任何的 method),但是空 interface 在我们需要存储任意类型的数值的时候相当有用,因
为它可以存储任意类型的数值。如下示例:
// 定义 a 为空接口 var a interface{} var i int = 5 s := "Hello world' // a 可以存储任意类型的数值 a = i a = s
一个函数把 interface{} 作为参数,则可以接受任意类型的值作为参数,如果一个函数返回 interface{} ,那么也就可以
返回任意类型的值
5.interface 变量存储的类型
我们知道 interface 的变量里面可以存储任意类型的数值(该类型实现了 interface),那么我们如何反向知道这个变量里面实际保存
的是哪个类型的对象?目前有两种方式:
5.1 Comma-ok 断言
Go 语言里面有一个语法,可以直接判断是否是该类型的变量:value,ok = element.(T),这里 value 就是变量的值,ok 是一个
bool 类型,element 是 interface 变量,T 是断言的类型
如果 element 里面确实存储了 T 类型的数值,那么 ok 返回 true,否则返回 false
如下示例:
package main import ( "fmt" "strconv" ) // 定义一个空接口 type Eelement interface {} type List [] Eelement // 定义一个 Person 结构体 type Person struct { name string age int } // 给 Person 绑定一个方法 func (p Person) String() string{ return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)" } func main() { list := make(List,3) list[0] = 1 // an int list[1] = "Hello" // a string list[2] = Person{"Dennis",70} for index,element := range list{ // 类型判断 if value,ok := element.(int);ok{ fmt.Printf("list[%d] is an int and its value is %d\n", index, value) }else if value,ok := element.(string);ok{ fmt.Printf("list[%d] is a string and its value is %s\n", index, value) }else if value,ok := element.(Person);ok{ fmt.Printf("list[%d] is a Person and its value is %s\n", index, value) }else{ fmt.Printf("list[%d] is of a different type\n", index) } } } ---------------------------------------------------------------------------------- 输出结果: list[0] is an int and its value is 1 list[1] is a string and its value is Hello list[2] is a Person and its value is (name: Dennis - age: 70 years)
5.2 type-switch
直接看示例:
package main import ( "fmt" "strconv" ) // 定义一个空接口 type Eelement interface {} type List [] Eelement // 定义一个 Person 结构体 type Person struct { name string age int } // 给 Person 绑定一个方法 func (p Person) String() string{ return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)" } func main() { list := make(List,3) list[0] = 1 // an int list[1] = "Hello" // a string list[2] = Person{"Dennis",70} for index,element := range list{ // 使用 type-switch 做类型判断 switch value := element.(type) { case int: fmt.Printf("list[%d] is an int and its value is %d\n", index, value) case string: fmt.Printf("list[%d] is a string and its value is %s\n", index, value) case Person: fmt.Printf("list[%d] is a Person and its value is %s\n", index, value) default: fmt.Println("list[%d] is of a different type", index) } } } ------------------------------------------------------------------------------------- 输出结果: list[0] is an int and its value is 1 list[1] is a string and its value is Hello list[2] is a Person and its value is (name: Dennis - age: 70 years)
6. 嵌入 interface
如果一个 interface1 作为 interface2 的一个嵌入字段,那么 interface2 隐式的包含了 interface1 里面的method
在源码包 container/heap 里面有这样的一个定义:
type Interface interface { sort.Interface Push(x interface{}) // add x as element Len() Pop() interface{} // remove and return element Len() - 1. }
看如上代码片段,我们看到 sort.Interface 其实就是嵌入字段,把 sort.Interface 的所有 method 给隐式的包含进来了,
即下面的方法:
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) }
另外一个就是 io 包下面的 io.ReadWriter ,它包含了 io 包下面的两个 interface:Reader、Writer
// ReadWriter is the interface that groups the basic Read and Write methods. type ReadWriter interface { Reader Writer }
下面再来看一个示例:
package main import "fmt" // 定义一个 USB interface type USB interface { Name() string // interface 嵌入 Connecter } // 定义一个 Connecter interface type Connecter interface { Connect() } // 定义一个 struct type PhoneConnecter struct { name string } // 给 PhoneConnecter 绑定 Name 方法 func (pc PhoneConnecter) Name() string{ return pc.name } // 绑定 Connect 方法 func (pc PhoneConnecter) Connect() { fmt.Println("Connected:",pc.name) } func main() { a := PhoneConnecter{"PhoneConnecter"} a.Name() a.Connect() }