golang 泛型与多态(方法,接口和匿名字段)

方法

func (s *Struct1)funcName(a,b int) (stringint) {
    ...
}
  • 方法比函数多了一个接受者

    • 方法作用于结构体或者自创类型
      • 类似c++里的成员函数
  • 方法的声明和使用者可以不在一个文件中,但要在一个包中

  • 两种类型的接收者:值接收者和指针接收者

    • 使用值类型接收者定义的方法,在调用的时候,使用的其实是值的副本,所以对该值的任何操作,不会影响原来的变量
    • 如果想要方法对接受者作出改变,那么就应该使用指针作为接受者 ,使用的是指针的副本
    • 可以类比为recevier是方法的第一个参数
    • 在使用的时候,值或者指针都可以直接调用,会自动根据声明取地址或者解引用
  • String方法

    • print函数在打印自定义数据类型时会自动调用其String方法
    • func (t T) String() string

接口

在这里插入图片描述

  • 如果一个类型实现了某一个接口的所有方法,则所有使用该接口的地方,都可以支持该类型的值
  • 不要使用一个指针指向一个接口类型,因为接口本身已经是一个指针
  • 接口调用时动态计算后的跳转调用,会导致CPU缓存失效和分支预测失败
  • 接口变量初始化才有意义,默认为nil
  • 空接口可以被任意类型实现
    • type empty interface{}
    • 所以利用空接口可以实现泛型
  • 编译器校验接口能否被赋值是比较二者方法集,而不是具体接口类型名
    • 如果A接口的方法集是B的超集,则A的接口变量可以直接赋值给B
  • 接口变量的底层实现
    • 接口的值是一个两个字长度的数据结构
    • 第一个字包含一个指向内部表结构的指针,这个内部表里存储接口类型,绑定的实例类型以及相关联的方法集
    • 第二个字包含的是一个指向存储的实体类型值的指针

在这里插入图片描述
在这里插入图片描述

  • 类型断言

    • 语法:i.(TypeName)
      • i必须为接口变量
    • 如果TypeName是具体类型,则为判断该接口绑定实例的类型是否是该具体类型
    • 如果TypeName是借口类型,则会判断该接口绑定实例是否同时实现了该接口类型
    type Element interface{
    	io.Reader
    }
    // element必须为借口类型
    var element Element = 1
    
    if value,ok := element.(int); ok {
        ...
    } 
    
    if iface, ok := element.(io.Reader); ok {
    	...
    }
    
  • 类型查询

    • 在处理来自于外部的、类型未知的数据时,比如解析诸如 JSON 或 XML 编码的数据时会非常有用
    • 语法:switch v := i.(type){}
      • i必须为接口变量
      • 如果i没有绑定任何实例,则v值为nil
    • case后面既可跟具体类型,也可跟接口类型
    • 不能使用fallthrough
    switch v := i.(type) {
    case nil:
    	...
    case string:
        ...
    }

	switch v:= i.(type) {
	case io.Reader, io.Writer:
		...
	}
  • 方法集
    • 值类型变量的方法集只包含值接受者声明的方法
    • 指针类型变量的方法集同时包含值和指针接受者声明的方法
// 指针声明的方法
func(u *User) notify() {
	...
}

type notifier interface {
	notify()
}

func sendNotify(n notifier) {
	n.notify
}

var u User
// 下面的函数编译不通过
// 因为User类型只实现了指针方法集,不能传入值
sendNotify(n)
// 下面的函数通过
sendNotify(&n)
// 上面这样规定的原因是编译器不是总能自动获得一个值得地址,例如这个值是一个常数25
、、

匿名字段

  • 结构体中只有类型名而没有变量名的字段
  • 于是该结构体继承了内嵌的匿名字段的内部字段和方法
    • 以此可以模拟继承
    • 可以直接调用内部类型的方法: outer.innerfunc()
    • 如果内部类型实现了某个接口,外部类型也自动实现了
  • 如果外部类型有和内部类型同名的方法,那么会覆盖内部方法
    • 此时如果要调用内部方法,就要显式地通过访问内部类型来访问
    • outer.inner.func()
  • 从一个现有的非 interface 类型创建新类型时,并不会继承原有的方法
    • 如果你需要使用原类型的方法,可将原类型以匿名字段的形式嵌到你定义的新 struct 中
// myMutex没有Lock()方法
type myMutex sync.Mutex
// myMutex有Lock()方法
type myLocker struct {
    sync.Mutex
}
发布了161 篇原创文章 · 获赞 19 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/winter_wu_1998/article/details/102734223