Go语言的接口是一种抽象数据类型,是一系列接口的集合,接口把所有的具有共性的方法定义在一起,任何其它类型只要实现了接口定义的方法就是实现了接口。接口是duck-type编程的一种体现,不关心属性(数据),只关心行为(方法)。
type 接口类型名 interface{
方法名1(参数列表)(返回值)
方法名2(参数列表)(返回值)
...
}
type Fruit interface{
Color(color string)
Num(n int)
}
在 go 中实现接口很简单,不需要显式的声明实现了某个接口,想要实现某个接口,只需要实现接口中的所有方法即可。
type Fruit interface {
Color() (color string)
Num() (n int)
}
type Apple struct {
Col string
N int
}
func (apple Apple) Color() (color string) {
fmt.Println(apple.Col)
return apple.Col
}
func (apple Apple) Num() (n int) {
fmt.Println(apple.N)
return apple.N
}
type Orange struct {
Col string
N int
}
func (orange Orange) Color() (color string) {
fmt.Println(orange.Col)
return orange.Col
}
func (orange Orange) Num() (n int) {
fmt.Println(orange.N)
return orange.N
}
上面定义了两个结构体类型和一个接口,下面我们通过程序把实现的类型和接口联系起来
可以将一个实现接口的对象实例赋值给接口,也可以将另外一个接口赋值给接口。
将一个类对象实例赋值给一个接口前,要保证类实现了接口的所有方法。
package main
import "fmt"
type Fruit interface {
Color() (color string)
Num() (n int)
}
type Apple struct {
Col string
N int
}
func (apple Apple) Color() (color string) {
fmt.Println(apple.Col)
return apple.Col
}
func (apple Apple) Num() (n int) {
fmt.Println(apple.N)
return apple.N
}
type Orange struct {
Col string
N int
}
func (orange Orange) Color() (color string) {
fmt.Println(orange.Col)
return orange.Col
}
func (orange Orange) Num() (n int) {
fmt.Println(orange.N)
return orange.N
}
func main() {
apple := Apple{"red", 10}
orange := Orange{"yellow", 5}
var fruit Fruit
fruit = apple
fruit.Color()
fruit.Num()
fruit = orange
orange.Color()
orange.Num()
}
程序运行之后结果如下:
接口变量存储了两部分信息,一个是分配给接口变量的具体值(接口实现者的值),一个是值的类型的描述器(接口实现者的类型),形式是(value, concrete type),而不是(value, interface type)。
实现接口的具体方法时,如果以指针作为接收者,接口的具体实现类型只能以指针方式使用,值接收者既可以按指针方式使用也可以按值方式使用。
package main
import "fmt"
type Retriever interface {
Get(url string) string
}
type MockRetriever struct {
Contents string
}
// 值接收者
func (r MockRetriever) Get(url string) string {
return r.Contents
}
type RealRetriever struct {
Contents string
}
// 指针接收者
func (r *RealRetriever) Get(url string) string {
return r.Contents
}
func main() {
var retriever Retriever
retriever = MockRetriever{"This is fake Retreiver"}
fmt.Printf("%T %v\n", retriever, retriever)
retriever = &MockRetriever{"This is fake Retreiver"}
fmt.Printf("%T %v\n", retriever, retriever)
retriever = &RealRetriever{"This is real Retriever"}
//retriever = RealRetriever{"This is real Retriever"} //error
fmt.Printf("%T %v\n", retriever, retriever)
}
空接口类型interface{}一个方法签名也不包含,所以所有的数据类型都实现了空接口。
空接口类型可以用于存储任意数据类型的实例。
如果一个函数的参数是空接口类型interface{},表明可以使用任何类型的数据。如果一个函数返回一个空接口类型,表明函数可以返回任何类型的数据。
interface{}可用于向函数传递任意类型的变量,但对于函数内部,该变量仍然为interface{}类型(空接口类型),而不是传入的实参类型。
利用接口类型作为参数可以达到抽象数据类型的目的。
interface{}可用于向函数传递任意类型的变量,但对于函数内部,该变量仍然为interface{}类型(空接口类型),而不是传入的实参类型。
类型断言是一个使用在接口值上的操作
有时候,我们可能需要知道某个接口类型的实际类型,比如某个方法需要接收多种类型的数据并需做分别处理时,我们可以把形参设为空接口类型以接收任意类型的值,但是我们怎么反向知道里面实际保存了的是哪个类型的对象呢?
接口类型向普通类型的转换称为类型断言(运行期确定)。
value,ok := x.(T)
x表示一个接口类型,T表示一个类型,也可以是接口类型。
该断言表达式会返回x的值和一个布尔值 可根据该布尔值判断x是否为T类型。
package main
import "fmt"
type Person struct {
Name string
Age int
}
type IntSlice []int
func Print1(obj interface{}) { //普通类型
value, ok := obj.(int)
if ok {
fmt.Println("int")
fmt.Println(value)
} else {
fmt.Println("type error")
}
}
func Print2(obj interface{}) { //结构体类型
value, ok := obj.(Person)
if ok {
fmt.Println("Person")
fmt.Println(value.Name, value.Age)
} else {
fmt.Println("type error")
}
}
func Print3(obj interface{}) { //切片类型
value, ok := obj.(IntSlice)
if ok {
fmt.Println("Slice")
fmt.Println(value)
} else {
fmt.Println("type error")
}
}
func main() {
var value int = 20
Print1(value)
person := Person{"jack", 25}
Print2(person)
slice := IntSlice{1, 2, 3, 4, 5}
Print3(slice)
}
程序运行结果:
断言失败在编译阶段不会报错,因此,如果不对断言结果进行判断将可能会断言失败导致运行错误。
不同类型变量的运算必须进行显式的类型转换,否者结果可能会溢出,导致出错。
类型断言也可以配合switch语句进行判断。
var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
default:
fmt.Printf("unexpected type %T", t) // %T prints whatever type t has
case bool:
fmt.Printf("boolean %t\n", t) // t has type bool
case int:
fmt.Printf("integer %d\n", t) // t has type int
case *bool:
fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
case *int:
fmt.Printf("pointer to integer %d\n", *t) // t has type *int
}