duck
type Pet interface {
SetName(name string)
Name() string
Category() string
}
type cat struct{
}
func (c *cat)SetName(name string){
}
func (c *cat)Name()string{
}
func (c *cat)CAtegory(){
}
因为Cat指针类型实现了Pet方法所以上面的变量pet赋值方式不会有报错,而以下的赋值方式则会报错
var pet Pet=&cat
var pet Pet = cat
对于一个接口类型的变量来说,例如上面的变量pet,我们赋给它的值可以被叫做它的实际值(也称动态值),而该值的类型可以被叫做这个变量的实际类型(也称动态类型)。
比如,我们把取址表达式&cat的结果值赋给了变量pet,这时这个结果值就是变量pet的动态值,而此结果值的类型*Cat就是该变量的动态类型。
动态类型这个叫法是相对于静态类型而言的。对于变量pet来讲,它的静态类型就是Pet,并且永远是Pet,但是它的动态类型却会随着我们赋给它的动态值而变化。
比如,只有我把一个Cat类型的值赋给变量pet之后,该变量的动态类型才会是Cat。如果还有一个Pet接口的实现类型Fish,并且我又把一个此类型的值赋给了pet,那么它的动态类型就会变为Fish。
还有,在我们给一个接口类型的变量赋予实际的值之前,它的动态类型是不存在的。
当我们为一个接口变量赋值时会发生什么
type Pet interface {
Name() string
Category() string
}
cat := Cat{"little pig"}
var pet Pet = cat
cat.SetName("monster")
如果我们使用一个变量给另外一个变量赋值,那么真正赋给后者的,并不是前者持有的那个值,而是该值的一个副本。 接口类型本身是无法被值化的。在我们赋予它实际的值之前,它的值一定会是nil,这也是它的零值。反过来讲,一旦它被赋予了某个实现类型的值,它的值就不再是nil了。不过要注意,即使我们像前面那样把cat的值赋给了pet,pet的值与cat的值也是不同的。这不仅仅是副本与原值的那种不同。当我们给一个接口变量赋值的时候,该变量的动态类型会与它的动态值一起被存储在一个专用的数据结构中。严格来讲,这样一个变量的值其实是这个专用数据结构的一个实例,而不是我们赋给该变量的那个实际的值。所以我才说,pet的值与dog的值肯定是不同的,无论是从它们存储的内容,还是存储的结构上来看都是如此。不过,我们可以认为,这时pet的值中包含了dog值的副本。
interface的实例会包含两个指针,一个是指向类型信息的指针,另一个是指向动态值的指针。这里的类型信息是由另一个专用数据结构的实例承载的,其中包含了动态值的类型,以及使它实现了接口的方法和调用它们的途径,等等。
总之,接口变量被赋予动态值的时候,存储的是包含了这个动态值的副本的一个结构更加复杂的值。