猿问

Go 如何将方法绑定到对象?

前几天刚开始学习 Go。今天,我们在调试一段代码时,发现了一些似乎违反 Go 直觉的事情。


首先,我们定义了一个接口和一个实现它的数据结构。


type Executer interface {

    Execute()

}


type whatever struct {

    name string

}


func (this *whatever) Execute() {

    log.Println(this.name)

}

现在考虑我有一个 nil 指针whatever,我尝试调用该方法Execute。在我迄今为止使用的其他面向对象语言中,这将在调用方法(即w.Execute())时调用空指针错误,因为对象指针为空。Execute有趣的是,在 Go 中,该方法被调用,当我尝试取消引用时,该方法出现空指针错误this.name。为什么不在调用方法的时候呢?


func main() {

    var w *whatever

    w.Execute()

}

那么,我现在想要了解的是这怎么可能?这是否意味着 Go 仅在编译时进行早期方法绑定,而在运行时没有方法与特定对象的绑定?


慕森王
浏览 250回答 1
1回答

汪汪一只猫

接收者只是函数的“普通”参数。普通参数可以是指针类型。如果是,则允许您nil作为参数传递,这是完全有效的。您需要记住的只是不要取消引用nil指针参数。这同样适用于特殊的接收器参数。如果它是一个指针,它可能是nil,你不能取消引用它。规范:方法声明:接收者是通过方法名称前面的额外参数部分指定的。...该方法被称为绑定到它的接收者基类型,并且方法名称仅在type或的选择器中可见。T*T允许nil接收器值不仅是不被禁止的,它还有实际用途。例如,请参阅测试嵌套结构中的 nil 值。null在 Java 中,您也可以在对象上调用静态方法。确实你不能在 Go 中做同样的事情,因为 Go 没有像static,public等修饰符private。在 Go 中只有导出和非导出的方法(由它们的名字的后一个暗示)。但是 Go 也提供了类似的东西:方法表达式和方法值。如果您有一个接收器类型的方法m,T则表达式T.m将是一个函数值,其签名包含m接收器类型“前缀”的参数和结果类型。例如:type Foo intfunc (f Foo) Bar(s string) string { return fmt.Sprint(s, f) }func main() {    fooBar := Foo.Bar // A function of type: func(Foo, string) string    res := fooBar(1, "Go")    fmt.Println(res)}Foo.Bar将是一个带有 type 的函数func (Foo, string) string,你可以像任何其他普通函数一样调用它;而且您还必须将接收器作为第一个参数传递。上面的应用程序输出(在Go Playground上试试):Go1稍微“前进”,我们不需要存储Foo.Bar在变量中,我们可以直接调用Foo.Bar:fmt.Println(Foo.Bar(1, "Go"))输出相同(在Go Playground上尝试)。现在这几乎看起来像staticJava 中的方法调用。而作为“最后”步骤,当我们对自身的值(而不是类型标识符)使用上述表达式时Foo,我们会得到方法值,这会保存接收者,因此方法值的类型不包括接收者,它的签名将是方法的签名,我们可以调用它而无需传递接收者:var f Foo = Foo(1)bar := f.Barfmt.Println(bar("Go"))这将再次输出相同的内容,请在Go Playground上尝试。
随时随地看视频慕课网APP

相关分类

Go
我要回答