【问题标题】:Why is an interface assignment more strict than a method call?为什么接口分配比方法调用更严格?
【发布时间】:2020-12-09 09:49:25
【问题描述】:

我正在经历围棋之旅。我了解到,如果我们有一个接受指针作为接收者的方法,它也会接受一个值类型作为接收者(go 会自动进行转换)。

type Vertex struct { X, Y float64 }

func (v *Vertex) Abs() float64 {
    return math.Sqrt(v.X * v.X + v.Y * v.Y)
}

那么下面的代码是有效的,无论是通过值还是指针接收Vertex

v := Vertex{1, 2}
fmt.Println(v.Abs())

p := &v
fmt.Println(p.Abs())

但是,假设我们有以下界面:

type Abser interface {
    Abs() float64
}

那么,为什么下面的代码无效呢?

var a Abser
v := Vertex{1, 2}

a = v // invalid

我的理解是这样就可以了。即使 v 是一个值类型,它“实现”了接受指针接收器的 Abs 函数,它也会按值接受它?

接口是否只是为了更严格地定义接口变量在右侧可以保存的内容而设计的?该接口将 *Vertex 和 Vertex 视为两种不同的类型,但是 Abs() 方法也没有问题处理。

【问题讨论】:

  • The Tour of Go 是学习语言的资源,但如果你想出一些你认为奇怪的东西:阅读语言规范!规范golang.org/ref/spec非常短(比典型语言规范短一个数量级)并且令人惊讶地易于理解。它解释了诸如“可分配性”(即在什么条件下x = y 有效或无效)、“方法集”如何计算以及在这种情况下 Go 将自动取消引用或自动获取地址等相关概念。阅读它。

标签: go interface


【解决方案1】:

在这两种情况下,Go 都需要一个指针来运行该方法。最大的不同是方法调用会自动获取v的地址,但检查是否实现了接口不会。

方法调用:

在普通类型上调用带有指针接收器的方法时,如果允许,Go 将自动获取地址。来自spec on method calls(重点是我的):

方法调用 x.m() 如果 x 的(类型)方法集是有效的 包含 m 并且参数列表可以分配给参数列表 米。 如果 x 是可寻址的并且 &x 的方法集包含 m,则 x.m() 是 (&x).m() 的简写

在这种情况下,x 引用您的变量 v 并且是 addressable。所以在方法调用时,Go 会自动运行 (&v).Abs()

作业:

当尝试assigna = v 时,必须填写的支票是T is an interface type and x implements T.v 仅在其 method set 与接口匹配时才实现 Abser。该方法集确定如下:

任何其他类型 T 的方法集由所有声明的方法组成 接收器类型为 T。对应指针类型的方法集 *T 是用接收者 *T 或 T 声明的所有方法的集合(即它还包含 T 的方法集)。

你会注意到,在计算方法集时,Go 并没有像在方法调用中那样获取v 的地址。 这意味着为var v Vertex设置的方法为空,未能实现接口。

解决方案:

解决办法是自己取v的地址:

var a Abser
v := Vertex{1, 2}

a = &v

通过这样做,您现在正在查看 *Vertex 的方法集,其中确实包含 Abs() float64 并因此实现了接口 Abser

【讨论】:

    猜你喜欢
    • 2011-10-13
    • 1970-01-01
    • 2017-12-19
    • 2010-11-07
    • 2013-07-09
    • 2020-06-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多