note interface

187 阅读3分钟

这是我参与更文挑战的第4天,活动详情查看:更文挑战

本文记录一下 interface 学习相关笔记

def say(interface):
    interface.hello()

上述代码,py 只有在运行期才判断 interface 这个类型有没有 hello()

Go 作为静态语言,在编译期就可以发现类型不匹配的错误,同时也没有显式要求声明实现 interface

值接受者/指针接受者

这里直接说结论(其实没什么好解释的。。。):

  1. 接收者 -> *Gopher -> 很可能在方法中会对接收者的属性进行更改操作,影响接受者属性
  2. 接收者 -> Gopher -> 在方法中不会对接收者本身产生影响
  3. 当实现了一个接收者是 值类型 的方法,就可以自动生成一个接收者是 对应指针类型 的方法,因为两者都不会影响接收者
  4. 最终结论:如果实现了接收者是值类型的方法,会隐含地也实现了接收者是指针类型的方法

什么时候使用?

  • *Gopher
  1. 方法需要修改接受者的值
  2. 接受者本身在拷贝过程中成本大,用指针拷贝更加高效
  • Gopher
  1. 类型是 Go 中内置的原始类型
  2. 看你习惯

类型系统

myslice 是自定义的 type myslice []stringslicetypeslice本身代表的type

这样其实就可以解释:

// string 类型别名
type mytype1 = string
// mytype2 自定义类型
type mytype2 string

注释里面也解释的很清楚,这两个类型是完全不一致的。对应的类型元数据也完全不一样了。

内部实现

ifaceeface 都是 Go 中描述接口的底层结构体:

  1. iface 描述的接口包含方法
  2. eface 则是不包含任何方法的空接口:interface{}

先说 iface

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

type iface struct {
	tab  *itab              // 接口类型以及赋给这个接口的实体类型
	data unsafe.Pointer     // 接口具体的值 -> 指向堆内存的指针
}

type itab struct {
	inter *interfacetype
	_type *_type            // 接口实际指向值的类型信息
	hash  uint32            // `_type.hash` 拷贝【类型断言可以快速判断,从而实现类型转换的判断】
	_     [4]byte
	fun   [1]uintptr        // 接口方法对应具体实现的方法地址,类比 `C++ 虚函数表`,实现接口调用的动态派发
}

了解了上面的数据结构,接下来看看 接口的构造过程

若干问题

记录一下涉及 interface 中的一些问题

转换中的 nil

type MyError struct {}

func (i MyError) Error() string {
	return "MyError"
}

func main() {
	err := Process()
	fmt.Println(err)

	fmt.Println(err == nil)
}

func Process() error {
	var err *MyError = nil
	return err
}

上面我们知道:

  1. struct MyError -> interface error (实现了 interface error)
  2. return *MyError 返回这个类型,但是值为 nil
  3. 上面得到:
    • 返回的 err 动态类型为:*MyError
    • 返回的动态值:nil

interface 接口值只有 动态类型动态值 都为 nil 时,才能说 interface == nil

所以上述的结果如下。因为动态类型是 *MyError,所以和 nil 不相等;但是值确为 nil,所以打印值为 nil

<nil>
false