Go接口

1.接口定义

接口是一个或多个方法签名的集合,任何类型的“方法集”中只要拥有与之对应的全部方法,
就表示它
"实现" 了该接口,无须在该类型上显式添加接口声明。
所谓对应方法,是指有相同名称、参数列表
(不包括参数名) 以及返回值。当然,该类型还
可以有其他方法。
接口命名习惯以 er 结尾,结构体。
接口只有方法签名,没有实现。
接口没有数据字段。
可在接口中嵌入其他接口。
类型可实现多个接口。

package main

import "fmt"

type Stringer interface{
    String() string
}

type Printer interface{
    Stringer //接口嵌入
    Print()
}


type User struct{
    id int
    name string
}

func (self *User) String() string{
    return fmt.Sprintf("user %d, %s",self.id,self.name)
}

func (self *User) Print() {
    fmt.Println(self.String())
}



func main() {
    
    var t Printer=&User{1,"Feng Wei"}
    t.Print()

}
输出:

user 1, Feng Wei

空接口 interface{} 没有任何方法签名,也就意味着任何类型都实现了空接口

package main

import "fmt"

func Print(v interface{}) {
    fmt.Printf("%T: %v\n",v,v)
}


func main() {
    
    Print(1)
    Print("Hello Feng Wei!")

}
输出:

int: 1
string: Hello Feng Wei!


2.执行机制

接口对象由接口表指针和数据指针构成,数据指针持有的是目标对象的只读复制品,复制完整对象或指针。

type User struct{
    id int
    name string
}


func main() {
    
    u:=User{1,"Tom"}
    var i interface{}=u

    u.id=2
    u.name="Jack"

    fmt.Printf("%v\n",u)
    fmt.Printf("%v\n",i.(User))

}
输出:

{2 Jack}
{1 Tom}


接口转型返回临时对象,只有使用指针才能修改其状态。

package main

import "fmt"



type User struct {
    id int
    name string
}

func main() {
    u := User{1, "Tom"}
    var vi, pi interface{} = u, &u

    // vi.(User).name = "Jack" // Error: cannot assign to vi.(User).name
    pi.(*User).name = "Jack"
    fmt.Printf("%v\n", vi.(User))
    fmt.Printf("%v\n", pi.(*User))
}
输出:

{1 Tom}
&{1 Jack}


只有 tab data 都为 nil 时,接口才等于 nil

package main

import (
    "fmt"
    "unsafe"
    "reflect"
)


func main() {
    var a interface{} = nil // tab = nil, data = nil
    var b interface{} = (*int)(nil) // tab 包含 *int 类型信息, data = nil
    type iface struct {
        itab, data uintptr
    }
    ia := *(*iface)(unsafe.Pointer(&a))
    ib := *(*iface)(unsafe.Pointer(&b))
    fmt.Println(a == nil, ia)
    fmt.Println(b == nil, ib, reflect.ValueOf(b).IsNil())
}



输出:

true {0 0}

false {4792000 0} true




3.接口转换

利用类型推断,可判断接口对象是否某个具体的接口或类型。

package main

import "fmt"

type Stringer interface{
    String() string
}

type Printer interface{
    Stringer //接口嵌入
    Print()
}




func (self *User) String() string{
    return fmt.Sprintf("user %d, %s",self.id,self.name)
}

func (self *User) Print() {
    fmt.Println(self.String())
}

type User struct {
    id int
    name string
}

func main() {
    var o interface{} = &User{1, "Tom"}
    if i, ok := o.(fmt.Stringer); ok { // ok-idiom
        fmt.Println(i)
    }
    u := o.(*User)
    // u := o.(User) // panic: interface is *main.User, not main.User
    fmt.Println(u)
}

输出:

user 1, Tom
user 1, Tom


4.接口技巧

让编译器检查,以确保某个类型实现接口。
var _ fmt.Stringer = (*Data)(nil)
某些时候,让函数直接 "实现" 接口能省不少事。

type Tester interface {
	Do()
}
type FuncDo func()
func (self FuncDo) Do() { self() }
func main() {
	var t Tester = FuncDo(func() { println("Hello, World!") })
	t.Do()
} 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值