为什么引用类型可以分配给不满足的接口?[golang]

huangapple go评论90阅读模式
英文:

Why reference type can be assigned to not satisfied interface [golang]

问题

我发现值类型和引用类型之间的接口可满足性令人困惑。我仍然无法弄清楚在Go语言中它是如何工作的。考虑下面这段程序:

type Counter1 int
type Counter2 int

func (c Counter1) String() string {

}

func (c *Counter2) String() string {

}

func main() {
    var c1 Counter1
    var c2 Counter2
    var i1 fmt.Stringer = &c1 // 赋值语句 1
    var i2 fmt.Stringer = c2 // 赋值语句 2
}

然而,赋值语句 1 能够正常工作,但赋值语句 2 却不能。我本来预期两者都不能工作:类型 Counter1 满足 fmt.Stringer 接口,但 *Counter1 类型不满足该接口。因此,&c1 不应该赋值给 i1;类型 Counter2 不满足 fmt.Stringer 接口,但 *Counter2 类型满足该接口,所以 c2 不应该赋值给 i2,实际上也确实不能。这个测试程序的结果对我来说没有意义。我原以为这可能是一种语法糖,Go编译器会自动在引用类型和值类型之间进行转换。然而,下面这段测试代码让我更加困惑:

type Test interface {
    f()
    g()
}

func (c Counter1) f() {
}

func (c *Counter1) g() {
}

func receiveTest(t Test) {
}

func main() {
    ......
    var c3 Counter1
    var p3 Test = &c3 // 能够工作
    receiveTest(&c3) // 能够工作
    receiveTest(c3) // 不能工作
}

既不是 *Counter1 类型,也不是 Counter1 类型满足 Test 接口。然而,第一和第二个赋值语句仍然能够正常工作。这对我来说没有意义。

英文:

I find interface satisfiability between value type and reference type confusing. I still cannot figure out how it works in golang. Consider this piece of program:

type Counter1 int
type Counter2 int

func (c Counter1) String() string {

}

func (c *Counter2) String() string {

}

func main() {
    var c1 Counter1
    var c2 Counter2
    var i1 fmt.Stringer = &c1 // assignment 1
    var i2 fmt.Stringer = c2 // assignment 2
}

However assignment 1 works but assignment 2 doesn't. I expected none of them works: Type Counter1 satisfies fmt.Stringer however type *Counter1 doesn't. So &c1 should not be assignable to i1; Type Counter2 doesn't satisfy fmt.Stringer however type *Counter2 does, so c2 should not be assignable to i2 and actually it cannot. The outcome of this test program doesn't make sense to me. I thought it might be some kind of syntactic sugar where go compiler converts between reference type and value type automatically. However the following piece of test code makes me more confused:

type Test interface {
    f()
    g()
}

func (c Counter1) f() {
}

func (c *Counter1) g() {
}

func receiveTest(t Test) {
}

func main() {
    ......
    var c3 Counter1
    var p3 Test = &c3 // works
    receiveTest(&c3) // works
    receiveTest(c3) // doesn't work
}

Neither type *Counter1 nor type Counter1 satisfies interface Test. However first and second assignment still works. It doesn't make sense to me.

答案1

得分: 3

在Go语言中,选择器表达式(x.f)表示值x(有时是*x)的字段或方法f

基本上,在Go语言中,如果一个方法有一个指针接收器,你不需要写&object.method(),而是可以直接写object.method()

因为在Go语言中接口是隐式的,只要对象本身不是指针,那么无论满足接口的方法是否有指针接收器,它们都可以用来满足接口。

所以,在你的例子中,Counter1结构体可以调用f()g()方法,但*Counter1只能调用g()方法。

英文:

> In Go, a selector expression (x.f) denotes the field or method f of the value x (or sometimes *x).

Basically, in Go, if you have a pointer receiver on a method, you do not have to write &object.method(), but you can just write object.method()

https://play.golang.org/p/nh8X-vwdfr

https://play.golang.org/p/1uKwZw6E-J

Because interfaces are implicit in Go, as long as the object itself is not a pointer, then it doesn't matter if the methods that satisfy the interface have pointer receivers or not, they will still be used to satisfy it.

So, in your example, a Counter1 struct can have both f() and g() methods called on it, but a *Counter1 can only have a g() method called on it.

huangapple
  • 本文由 发表于 2016年9月15日 10:59:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/39502493.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定