接口
一组method 签名的组合 通过interface 来定义对象的一组行为
interface 就是一组抽象方法的集合
interface 类型
interface 类型定义了一组方法, 如果某个对象实现了某个接口中的方法, 则此对象就实现了此接口
interface 值
如果定义了一个interface 变量, 那么这个变量里面可以存实现这个interface的任意类型的对象。
空interface
interface{} 不包含任何的method
所有的类型都实现了空的interface
空interface 可以用来存储任意类型的值
一个函数把interface{} 作为参数 可以接受任意类型的值作为参数, 如果一个函数返回interface{ 那么也就可以返回任意类型的值
interface 变量存储的类型
interface 变量中国可以存储任意类型对象的数值(前提是实现了 interface)
反过来要想通过这个变量里面 实际保存的是哪个类型的对象呢 ?
-
断言 comma-ok 断言
value, ok = element.(T)
value 变量的值,ok 是bool 类型, element 是 interface 变量, T 是断言的类型
如果element 里面确实存储了T 类型的数值, 那么ok 返回true 否则false
package main
import (
"fmt"
"strconv"
)
type Element interface{}
type List [] Element
type Person struct {
name string
age int
}
//定义了String方法,实现了fmt.Stringer
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)
}
}
}
swith 测试
element.(type)
这种方式需要注意:
element.(type) 不能再switch 外的任何逻辑里面使用, 如果再外面使用就使用 断言comma-ok的方式
package main
import (
"fmt"
"strconv"
)
type Element interface{}
type List [] Element
type Person struct {
name string
age int
}
//打印
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{
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)
}
}
}
非侵入式接口
只需要实现接口中的所有方法 则就实现了这个接口
好处 :
1 go 语言的标准库 再也不需要回执类库的继承树图
2实现类的时候 只需要关心自己实现哪些方法, 不需要再纠结接口需要才分的多细才合理
3 不用为了实现一个接口而导入一个包。 多导入一个包 就以为着更多的耦合。
接口赋值
接口赋值在go 语言中 分为如下两种情况
1 将对象实例赋值为接口
2 将接口赋值给另一个接口
go语言中, 只要两个接口拥有相同的方法列表 (次序不同不要紧) 那么他们就是相同的, 可以相互赋值。
接口赋值 不需要两个接口必须等价。 如果接口A的方法列表 是B接口方法列表的子集, 那么接口B 可以赋值给接口A。
接口查询
var file1 Writer = …
if file5, ok := file1.(two.IStream); ok {
…
}
if 语句检查file1 接口指向 的对象实例是否实现two.IStream 接口, 如果实现了 执行特定的代码
接口查询 是否成功 要在运行期才能够确定
接口赋值, 编译器只需要通过静态类型检查就可以
Any 类型
go 中任何对象实例都满足空接口interface interface{ 都可以指向任何对象的Any 类型}
var v1 interface{} = 1 // 将int类型赋值给interface{}
var v2 interface{} = “abc” // 将string类型赋值给interface{}
var v3 interface{} = &v2 // 将*interface{}类型赋值给interface{}
var v4 interface{} = struct{ X int }{1}
var v5 interface{} = &struct{ X int }{1}
注意地方
如果接受方法 使用的是指针 的话 在判断类型的时候
_,ok := talk.(*myTalk)
类型断言
接口类型向普通类型的转换称为类型断言 (运行时绑定)
func echoArray(a interface{}){
b,_:=a.([]int)//通过断言实现类型转换
for _,v:=range b{
fmt.Print(v," ")
}
fmt.Println()
return
}
// 使用这样判断
b,ok:=a.([]int)
if ok{
...
}