---
title: Golang Interfaces
date: 2018-09-27
updated: 2020-07-19
---
# Interfaces
Interface:
1. Interface 可以用来模拟泛型,但是是运行时模拟,没有编译时快
1. method 没有 public protecte..., 如果要被其它 package 使用, 函数名首字母必须大写!
3. 只有方法签名,没有实现

## 接口嵌套

    type Stringer interface {
        String() string
    }
    type Printer interface {
        Stringer
        Print() 
    }

## 接口是一种特殊的struct 类型
interface 只能定义方法占位,不能定义属性

    type I interface{}
    var i I //ok
    i:=I{} //error

数组interface 可以当作Struct 来实例化(但是值不是为nil, 而是数组), 等价于数组属于该接口

    type A []interface{}
    var a A //ok
    a:=A{} //ok

实例: 接口数组, 每个元素是一个接口类型

    type Container []interface{}
    container:=&Container{}

参考:go generic
## 用作变量或结构体成员

    type Tester struct {
        s interface {
            String() string
        }
    }
    type User struct {
        id   int
        name string 
    }

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

    func main() {
        t := Tester{&User{1, "Tom"}}
        fmt.Println(t.s.String())
    }

## nil interface
A nil interface value holds neither value nor concrete type.

    type I interface { M() }
    var i I 
    fmt.Printf("(%v, %T)\n", i, i) //(<nil>, <nil>)

# 执行机制:tab,data,nil
接⼝口对象由接⼝口表 (interface table) 指针和数据指针组成。

    //runtime.h
    struct Itab {
        InterfaceType*    inter;
        Type*             type;
        void (*fun[])(void);
    };

    struct Iface {
        Itab* tab;
        void*    data;
    };

接口表`Itab`存储元数据信息,包括接⼝口类型、动态类型,以及实现接⼝口的⽅方法指针。无论是反射还是通过接⼝口调⽤用⽅方法,都会⽤用到这些信息。

数据指针持有的是目标对象的只读复制品,复制完整对象或指针。

    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)   // 2 Jack
        fmt.Printf("%v\n", i.(User))    //1 Tom
    }

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

    u := User{}
    var vi, pi interface{} = u, &u
    // vi.(User).name = "Jack"          //Error: Error: cannot assign to vi.(User).name
    pi.(*User).name = "Jack"

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

    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())

output:

    true  {0 0}
    false {505728 0} true

# Type assertions
assert type via `interface.(Type)`, 必是`interface`:
assert type via `interface.(interfaceType)`, 必是`interface`:
assert `*type` via `interface.(*interfaceType)`

## Type switches
语法

    //(s T)Init(){}
    t, ok := i.(T)

    //(s *T)Init(){}
    t, ok:= i.(*T)

省略语法ok (不匹配会报panic)

    t := i.(T)
    i.(T).Name

    for index, roleID := range delIdsIt.([]string) {
    }

example

    var i interface{} = "hello"

    s, ok := i.(string)
    fmt.Println(s, ok)  //hello true

    f, ok := i.(float64)
    fmt.Println(f, ok)// 0 false

可以省略ok(`o` 为interface. 下面是引用类型的接口

    var o interface{} = &User{1, "Tom"}
    u := o.(*User)  //no error
      	// u ,ok:= o.(User) u=User{0}, ok=false
        // u := o.(User) panic


## Type assert
switch 就可以不用返回ok, 还可以判断`case T`

    func do(i interface{}) {
        switch v := i.(type) {
        case int:
            fmt.Printf("Twice %v is %v\n", v, v*2)
        case string:
            fmt.Printf("%q is %v bytes long\n", v, len(v))
        default:
            fmt.Printf("I don't know about type %T!\n", v)
        }
    }

    func main() {
        do(21)
        do("hello")
        do(true)
    }

## 匿名结构体 匹配

    func f(v interface{}){
        id := v.(struct{Id int}).Id
    }

    f(struct{Id int}{1})    //match

    type T struct{Id int}   //error, type dismatch
    f(T{1})

## 超集vs子集
超集接⼝对象可转换为⼦子集接⼝口,反之出错。

    type Stringer interface {
        String() string
    }
    type Printer interface {
        String() string
        Print() 
    }

    type User struct {
        id   int
        name string }

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

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

    func main() {
        var o Printer = &User{1, "Tom"}
        var s Stringer = o              //Pointer 转Stringer
        fmt.Println(s.String())
    }

## new error
    func ReturnError() (string, error){       
        return "", fmt.Errorf("this is an %s error", "internal server")
        // or
        return "", errors.New("this is an error")
    }

## any type
    // setField sets field of v with given name to given value.
    func setField(v interface{}, name string, value string) error {
        // v must be a pointer to a struct
        rv := reflect.ValueOf(v)
        if rv.Kind() != reflect.Ptr || rv.Elem().Kind() != reflect.Struct {
            return errors.New("v must be pointer to struct")
        }

        // Dereference pointer
        rv = rv.Elem()

        // Lookup field by name
        fv := rv.FieldByName(name)
        if !fv.IsValid() {
            return fmt.Errorf("not a field name: %s", name)
        }

        // Field must be exported
        if !fv.CanSet() {
            return fmt.Errorf("cannot set field %s", name)
        }

        // We expect a string field
        if fv.Kind() != reflect.String {
            return fmt.Errorf("%s is not a string field", name)
        }

        // Set the value
        fv.SetString(value)
        return nil
    }

### get property

    func getField(v interface{}, field string) interface{} {
        r := reflect.ValueOf(v)
        f := reflect.Indirect(r).FieldByName(field)
        switch f.Kind() { 
            case reflect.String:
                fmt.Println(f.String())
            case reflect.Int:
                fmt.Println(f.Int())
            case reflect.Ptr:
                fmt.Printf("%v", f.Elem())
        }

        return f
    }
    //return f.Int()

### get type name

    var i interface{} = 1
    reflect.TypeOf(i).Name()
    reflect.TypeOf(i).String() 等价

### loop value

    v := reflect.ValueOf(person{"steve", 30})
    count := v.NumField()
    for i := 0; i < count; i++ {
        f := v.Field(i)
        switch f.Kind() {
            case reflect.String:
                fmt.Println(f.String()) //steve
            case reflect.Int:
                fmt.Println(f.Int())    //30
        }
    }

## 接口检查
    //查是否有string() 方法
    var _ fmt.Stringer = (*Data)(nil)

## 函数接口
为函数实现接口方法

    type Tester interface {
        Do()
    }
    type FuncDo func()
    func (self FuncDo) Do() { 
        self() 
    }

    func main() {
        // FunDo 是用于创建函数值的
        var t Tester = FuncDo(func() { println("Hello, World!") })
        t.Do()
    }

# built-in interface 
## Stringer(like js toString)
One of the most ubiquitous interfaces is Stringer defined by the fmt package.

    type Stringer interface {
        String() string
    }

A Stringer is a type that can describe itself as a string. The fmt package (and many others) look for this interface to print values.

    type Person struct {
        Name string
        Age  int
    }

    func (p Person) String() string {
        return fmt.Sprintf("%v (%v years)", p.Name, p.Age); 
    }

  	a := Person{"Arthur Dent", 42}
  	z := Person{"Zaphod Beeblebrox", 9001}
    fmt.Println(a, z)

注意:type 要对的上! 不要这样

    func (p *Person) String() string {}
    fmt.Println(a)

## Error
built in Error

    type error interface {
        Error() string
    }
    i, err := strconv.Atoi("42")
    if err != nil {
        fmt.Printf("couldn't convert number: %v\n", err)
        return
    }

custom Error:


    package main

    import (
        "fmt"
        "time"
    )

    type MyError struct {
        When time.Time
        What string
    }

    func (e *MyError) Error() string {
        return fmt.Sprintf("at %v, %s",
            e.When, e.What)
    }

    func run() error {
        return &MyError{
            time.Now(),
            "it didn't work",
        }
    }

    func main() {
        if err := run(); err != nil {
            fmt.Println(err)
        }
    }
  1. 笔记