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
}