go入门6 -- 接口

接口是Go类型语言中数据类型的关键。接口是一个或多个方法签名的集合,对于任何类型,只要拥有以这个接口对应的全部方法,就说明该类型实现了这个接口,无需在该类型上显示添加接口声明。

对应的方法是指具有相同的名称,参数列表,以及返回值。除此之外,该类型还可以有其他。

说一下接口的特点:

  • 接口命名一般习惯以er结尾,结构体;
  • 接口只有方法的签名,并非是实现了某方法
  • 接口没有数据字段
  • 在接口中可以嵌套其他接口
  • 类型可以实现多个接口
type User struct {
    id   int
    name string
}

func (u *User) TellId() {
    fmt.Println(u.id)
}

func (u *User) String() string {
    return fmt.Sprintf("user:%d", u.id)
}

func (u *User) TellName() {
    fmt.Println(u.name)
}

type Stringer interface {
    String() string
}

type Printer interface {
    TellId()
}
type UUer interface {
    Printer
    TellName()
}

func main() {
    var user = &User{10, "goland"}
    var user1 Printer = &User{1, "tom"}
    var user2 Stringer = &User{2, "jason"}
    var user3 UUer = &User{3, "jack ma"}
    user1.TellId()              // 1
    fmt.Println(user2.String()) // user:2
    user3.TellId()              // 3
    //fmt.Println(user3.String()) // user3.String undefined (type UUer has no field or method String)
    fmt.Println(user.String()) // user:10
    user.TellId()              // 10
    user.TellName()            // goland
    //userToUUer := UUer(user)
    //userToUUer.String()  // userToUUer.String undefined (type UUer has no field or method String)
}

从上述栗子中可以看出,User实现了Stringer, Printer, UUer的所有接口,因此user的实例可以调用所有的方法,并且同时可以看出,接口是可以嵌套的

另外可以看出超集接口可以转化为子集接口,反过来去不行,一旦转化为子集接口,则该类型只可以使用子集方法。

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

我们可以利用interface进行接口类型判断,注意这个里面是不支持fallthrough。

type User struct {
    id   int
    name string
}

func main() {
    var m interface{} = &User{1, "jack ma"}
    switch v := m.(type) {
    case nil:
        fmt.Println("nil")
    case fmt.Stringer:  // 因为User没有实现String()string方法,因为没实现这个接口
        fmt.Println("string", v)
    case func() string:
        fmt.Println("func")
    case *User:
        fmt.Println("User")
    default:
        fmt.Println("i do not know")
    }
}// 最终打印User

关于执行机制,接口对象由接口表(interface table)指针和数据指针组成,部分源码文件如下,根据 interface 是否含有方法,低层实现用了下述两个 struct 来进行表示, iface代表non-empty,这个里面包含实现的方法, eface代表是empty,就是里面不包含方法,这个编译器会自动帮助我们选择。从这里面可以看到,interface实际上有两个成员,tab和data,tab指向的是虚表,data则指向实际引用数据

type iface struct {
    tab  *itab
    data unsafe.Pointer
}

type eface struct {
    _type *_type
    data  unsafe.Pointer
}

经过上述分析,我们就可以判定interface与nil的关系

type State struct{}

func testnil1(a, b interface{}) bool {
    return a == b
}

func testnil2(a *State, b interface{}) bool {
    return a == b
}

func testnil3(a interface{}) bool {
    return a == nil
}

func testnil4(a *State) bool {
    return a == nil
}

func testnil5(a interface{}) bool {
    v := reflect.ValueOf(a)
    return !v.IsValid() || v.IsNil()
}

func main() {
    var a *State
    fmt.Println(testnil1(a, nil)) // false
    fmt.Println(testnil2(a, nil)) // false
    fmt.Println(testnil3(a))      //false
    fmt.Println(testnil4(a))      // true
    fmt.Println(testnil5(a))      // true
}

interface{}一共包含两个指针,一个指向值的类型,另外一个指向值的类型,对一个interface{}类型的nil变量来说,它的两个指针都是0;但是var a *State传进去后,指向的类型的指针不为0了,因为有类型了, 所以比较为false。 interface 类型比较, 要是两个指针都相等, 才能相等。

posted @ 2020-01-02 16:11  灬灬灬灬灬灬  阅读(150)  评论(0编辑  收藏  举报