Go:接口方法调用

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

Go: interface method call

问题

Q1: Is it ok to write L(1) as "car, _ := col.(Car)"?

Q2: L(2) prints "white" not "yellow".

Why is it? L(3) seems print "black" correctly.

Thanks.

英文:

I have two questions regarding Go interface from the following code.

type Color interface {
	getColor() string
	setColor(string)
}

type Car struct {
	color string
}
func (c Car) getColor() string {
	return c.color
}
func (c Car) setColor(s string) {
	c.color = s
}

func main() {
	car := Car{"white"}
	col := Color(car)

	car = col.(Car)         // L(1)
	car.setColor("yellow")
	fmt.Println(col)        // L(2)
	fmt.Println(car)
	car.color = "black"
	fmt.Println(col)        // L(3)
	fmt.Println(car)
}

Q1: Is it ok to write L(1) as "car, _ := col.(Car)"?

Q2: L(2) prints "white" not "yellow".

Why is it? L(3) seems print "black" correctly.

Thanks.

答案1

得分: 10

Q1:

不,你不能说car,_ := col.(Car)。原因并不是很明显。以下是L1中可以接受的语句列表:

car,ok := col.(Car)
car = col.(Car)
car,_ = col.(Car)
_,ok := col.(Car)

":="是声明/赋值的简写形式,因为car已经在该作用域中声明过了,所以使用:=会报错(":=左边没有新变量")。在这里放置"ok"声明了一个新变量("ok"),然而,下划线/忽略伪变量在:=的目的上不算作新变量。

编辑:为了明确,你可以在这里放置"ok"或者下划线,因为类型断言返回的是类型断言的值和一个布尔值,指示断言是否成功。如果问题实际上是关于"_"的一般情况,而不是关于":="运算符的问题:不,一般情况下你不能这样做

a,_ := 5

因为该语句只返回一个值,go不会让你忽略任何东西。(你会得到错误:"assignment count mismatch 2 = 1")。

Q2:

在Go中,方法可以在指针或值/基本类型上。我相信以下代码将正常工作:

car.setColor("yellow")
//...
func (car Car) setColor(s string) {
    car.color = s
    fmt.Println(car.color)
}

在这段代码中,它将正确打印"yellow"。这是因为你通过传递了方法接收器。实际上,它确实修改了car,但是修改的是你传递给它的另一辆车,这辆车恰好是你调用该方法的那辆车的完美副本。要修复这个问题,你需要一个指针接收器:

func (car *Car) setColor(s string) {
    car.color = s
}

这将使调用后的更改可见,因为你给方法提供了car所在的位置,而不仅仅是它拥有的数据。要全面起见,还有一些涉及"引用类型"(映射、切片、通道)的情况,你可以在非指针方法之外看到副作用,但这些是规则的例外情况。

请注意,如果你给这个方法一个指针接收器,类型为Car的变量将不再实现接口Color。相反,实现接口的类型是*Car(指向Car的指针)。实际上,由于指针在Go中是透明的,即使你将getColor保留为非指针接收器,这也是正确的,但最好的做法是在实现接口的类型上的所有方法都使用指针或基本类型,而不是混合使用。

还有一个杂项注意事项,因为你似乎在学习:以小写字母开头的setColor和getColor并没有错。然而,请注意,这些方法在你编写的包之外将不可用。要使它们可见,它们必须以大写字母开头。

英文:

Q1:

No, you cannot say car,_ := col.(Car). The reason for this isn't quite obvious. Here's a list of okay statements in L1:

car,ok := col.(Car)
car = col.(Car)
car,_ = col.(Car)
_,ok := col.(Car)

":=" is the short form for declaration/assignment, since car was already declared in that scope, := will give you an error ("no new variable on left side of :="). Putting "ok" there declares a new variable ("ok"), however, the underscore/ignore pseudo-variable does not count as a new variable for purposes of :=.

Edit: To be clear, you can put "ok" or the underscore here because type assertions return both the type-asserted value and a boolean value indicating whether the assertion was successful. If the question was actually about the general case of "_" and not a question on the ":=" operator: no, in the general case you can't do something like

a,_ := 5

Because that statement only returns one value and go won't let you ignore nothing. (You'll get the error: "assignment count mismatch 2 = 1").

Q2:

In Go, methods can be on a pointer or a value/base type. I believe you'll find the following will work fine:

car.setColor("yellow")
//...
func (car Car) setColor(s string) {
    car.color = s
    fmt.Println(car.color)
}

In this code, it will correctly print "yellow". This is because you're passing the method receiver by value. It does, in fact, modify car -- but a different car than you passed it, a car that happens to be a perfect copy of the car that you called the method on. To fix this you need a pointer receiver,

func (car *Car) setColor(s string) {
    car.color = s
}

This will make the changes visible after the call, because you're giving the method the location where car resides, not just the data that it has. To be thorough, there are a handful of cases involving "reference types" (maps, slices, channels) where you can see the side-effects outside a non-pointer method, but these are exceptions to the rule.

Do note that if you give this method a pointer receiver, variables of the type Car no longer implement the interface Color. Instead, the type that implements the interface is *Car (a pointer to a Car). In fact, since pointers are transparent in Go, this is true even if you leave getColor with a non-pointer receiver, but it's usually better form to make all methods on a type implementing an interface on either the pointer or the base type, not a mix of both.

One miscellaneous note since you appear to be learning: there's nothing wrong, per se, with beginning setColor and getColor with lowercase letters. However, be aware that these methods will not be available outside the immediate package you're writing. For them to be visible, they must start with an Uppercase letter.

答案2

得分: 2

为了使setColor能够改变你期望的Car对象,你必须传递一个指针,你的代码传递了一个Car值,并改变了该值的颜色,然后在方法返回时立即丢弃了该Car值的副本。

以下是你的示例更改后,使接口满足Car指针:

func (c *Car) getColor() string {
    return c.color
}
func (c *Car) setColor(s string) {
    c.color = s
}

上面的链接输出:

&{yellow}
&{yellow}
&{black}
&{black}
英文:

In order for setColor to mutate the Car object you expect you have to pass a pointer, your code passed a Car by value, and changes the color of that value, then promptly discarded that copy of the Car value when the method returns

Here is your example changed so that the interface is satisfied by a pointer to Car

func (c *Car) getColor() string {
    return c.color
}
func (c *Car) setColor(s string) {
    c.color = s
}

The link above outputs:

&{yellow}
&{yellow}
&{black}
&{black}

huangapple
  • 本文由 发表于 2013年7月31日 12:41:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/17961761.html
匿名

发表评论

匿名网友

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

确定