在Go中为接口分配指针时,会混淆隐式指针解除引用

Lec*_*eNi 3 pointers interface go

我是Go的新手,我正在研究它的界面功能.

这是代码:

package main

import (
        "fmt"
        "reflect"
)

type Integer int

func (a Integer) Less(b Integer) bool {
        return a < b
}

func (a *Integer) Add(b Integer) {
        *a += b
}

type LessAdder interface {
        Less(b Integer) bool
        Add(b Integer)
}

var a Integer = 1

var b LessAdder = &a

func main() {
        fmt.Println(reflect.TypeOf(b))
        fmt.Println(b.Less(2))
        b.Add(a)
        fmt.Println(a)

}
Run Code Online (Sandbox Code Playgroud)

它将输出以下内容:

*main.Integer
true
2
Run Code Online (Sandbox Code Playgroud)

嗯,这很好用.

要点是:如何var b LessAdder = &a运作.指针自动取消引用是在这里发生,还是在b调用成员方法时?

输出*main.Integer告诉我们b是指向类型的指针Integer,因此它是第二种情况.

然后棘手的事情来了:当我添加fmt.Pringln(*b)代码时,编译器出现错误:

 demo/demo1
./demo1.go:31: invalid indirect of b (type LessAdder)
Run Code Online (Sandbox Code Playgroud)

它让我很困惑.既然b是指针类型Integer,那么取消引用它应该有效.但为什么不呢?

icz*_*cza 7

你的最后一句话:

"既然b是指针类型Integer,那么取消引用它应该有效."

停在那儿.b不是指针类型的变量,因此你不能解引用它.

它是一个接口类型的变量,它是一对值和一个类型(值,类型),保持&a为值和*Integer类型(博客文章反射的法则,部分接口的表示).

这是指针类型变量的声明,*Integer:

var ip *Integer
Run Code Online (Sandbox Code Playgroud)

这是一种接口类型:

var intf LessAdder
Run Code Online (Sandbox Code Playgroud)

当你这样做:

var b LessAdder = &a
Run Code Online (Sandbox Code Playgroud)

会发生什么是LessAdder自动/隐式创建接口值(类型),它将保存值&a(和类型*Integer).这是一个有效的操作,因为的类型&a(这是*Integer)实现接口LessAdder:该方法集*Integer是接口的一个超集LessAdder(在这种情况下,它们是相等的,一个接口类型的方法集为其接口).

现在当你调用时b.Less(2),由于Less()有一个值接收器,指针将被解除引用,并且将生成指向值的副本并使用/传递作为方法的值接收器Less().

fmt.Println(reflect.TypeOf(b))不会撒谎,但它会打印动态类型b.b确实是动态类型*Integer,但静态类型bLessAdder静态类型,它决定了你可以用一个值做什么,以及允许哪些操作符或方法.