这是我参与更文挑战的第4天,活动详情查看:更文挑战
本文记录一下
interface学习相关笔记
def say(interface):
interface.hello()
上述代码,py 只有在运行期才判断 interface 这个类型有没有 hello()。
而Go 作为静态语言,在编译期就可以发现类型不匹配的错误,同时也没有显式要求声明实现 interface。
值接受者/指针接受者
这里直接说结论(其实没什么好解释的。。。):
- 接收者 ->
*Gopher-> 很可能在方法中会对接收者的属性进行更改操作,影响接受者属性 - 接收者 ->
Gopher-> 在方法中不会对接收者本身产生影响 - 当实现了一个接收者是 值类型 的方法,就可以自动生成一个接收者是 对应指针类型 的方法,因为两者都不会影响接收者
- 最终结论:如果实现了接收者是值类型的方法,会隐含地也实现了接收者是指针类型的方法
什么时候使用?
*Gopher
- 方法需要修改接受者的值
- 接受者本身在拷贝过程中成本大,用指针拷贝更加高效
Gopher
- 类型是
Go中内置的原始类型 - 看你习惯
类型系统
myslice 是自定义的 type myslice []string,slicetype 是 slice本身代表的type
这样其实就可以解释:
// string 类型别名
type mytype1 = string
// mytype2 自定义类型
type mytype2 string
注释里面也解释的很清楚,这两个类型是完全不一致的。对应的类型元数据也完全不一样了。
内部实现
iface 和 eface 都是 Go 中描述接口的底层结构体:
iface描述的接口包含方法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
}
上面我们知道:
struct MyError->interface error(实现了interface error)return *MyError返回这个类型,但是值为nil- 上面得到:
- 返回的
err动态类型为:*MyError - 返回的动态值:
nil
- 返回的
interface接口值只有 动态类型 和 动态值 都为nil时,才能说interface == nil
所以上述的结果如下。因为动态类型是 *MyError,所以和 nil 不相等;但是值确为 nil,所以打印值为 nil:
<nil>
false