为什么我无法在接口中访问这个字段?

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

Why can't I access this field in an interface?

问题

我正在尝试更好地理解接口,并且不理解为什么 s 没有 Width 字段。我的示例在这里

package main

import "fmt"

type shapes interface {
	setWidth(float64)
}

type rect struct {
	Width float64
}

func (r *rect) setWidth(w float64) {
	r.Width = w
}

var allShapes = map[string]shapes{
	"rect": &rect{},
}

func main() {
	r := &rect{}
	r.setWidth(5)
	fmt.Println(r.Width) // 这个可以工作
	for _, s := range allShapes {
		s.setWidth(7)
		fmt.Println(s.Width) // 为什么不行???
	}
}

为什么 rWidths 没有呢?我得到的确切错误是:

s.Width undefined (type shapes has no field or method Width)
英文:

I am trying to understand interfaces better and am not understanding why s has no field Width. My example is here:

package main

import "fmt"

type shapes interface {
	setWidth(float64)
}

type rect struct {
	Width float64
}

func (r *rect) setWidth(w float64) {
	r.Width = w
}

var allShapes = map[string]shapes{
	"rect": &rect{},
}

func main() {
	r := &rect{}
	r.setWidth(5)
	fmt.Println(r.Width)  // this works
	for _, s := range allShapes {
		s.setWidth(7)
		fmt.Println(s.Width) // why not???
	}
}

Why does r have Width but s doesn't? The exact error I get is:

s.Width undefined (type shapes has no field or method Width)

答案1

得分: 6

shapes接口是*rect实现的,但它不是一个具体的类型*rect。就像任何接口一样,它是一组方法,允许满足它的任何类型通过,就像给它一个临时的访客贴纸让它上楼一样。

例如,如果有一只猴子(或者值得一提的是,一只海豚)可以像人类一样行动和做任何事情,在Go的大楼里,它可以通过保安并乘电梯上楼。然而,这并不意味着它在基因上是人类。

Go是静态类型的,这意味着即使两个具有相同底层类型的类型,也不能在没有类型断言或有意识地转换类型的情况下动态地将它们转换或强制转换为彼此。

var a int
type myInt int
var b myInt

a = 2
b = 3
b = a         // 错误!无法将a(类型为int)分配给myInt类型。
b = myInt(a)  // 这是可以的。

请想象一下这种情况:

type MyInt int
type YourInt int

type EveryInt interface {
        addableByInt(a int) bool
}

func (i MyInt) addableByInt(a int) bool {
	// 无论逻辑如何,都不重要
	return true
}


func (i YourInt) addableByInt(a int) bool {
	// 无论逻辑如何,都不重要
	return true
}

func main() {
	// 两个人想要被视为int类型
	b := MyInt(7)
	c := YourInt(2)
	
	// 做所有`EveryInt`要求的事情
	// 并伪装成一个
	bi := EveryInt(b)
	ci := EveryInt(c)
	
	// 嘿,看,我们是一样的!这是我们能接近成为int的方式!
	bi = ci          // 这是可以的,我们是EveryInt兄弟会
	fmt.Println(bi)  // bi现在是2
	
	// 现在一个真正的int出现了,说
	// “嘿,你们两个看起来像我们中的一个!”
	var i int
	i = bi           // 糟糕!bi已经被制造出来了
	
	// ci此时逃走了

}

现在回到你的场景——想象一个实现shapes*circle出现了:

type circle struct {
        Radius float64
}

func (c *circle) setWidth(w float64) {
        c.Radius = w
}

*circle完全可以作为shapes传递,但它没有Width属性,因为它不是*rect。接口不能直接访问底层类型的属性,但只能通过实现的方法集来访问。为了访问属性,需要对接口进行类型断言,使实例成为一个具体类型:

var r *rect

// 验证`s`实际上是底层的`*rect`
if r, ok := s.(*rect); ok {
        fmt.Println(r.Width)
}

这就是为什么像Go这样的静态类型语言总是比动态类型的对应物更快的核心原因,后者几乎总是使用某种反射来动态处理类型强制转换。

英文:

shapes interface is what *rect implements, but it is not a concrete type *rect. It is, like any interface, a set of methods allowing any type satisfying it to pass, like giving a temporary visitor sticker to it to go up the building.

For instance, if there is a monkey (or for what it's worth, a dolphin) who can act and do everything a human can, in Go's building, he can pass the guard and go up the elevator. However, that doesn't make him genetically human.

Go is statically-typed, meaning even two types with the same underlying type cannot be dynamically converted or coerced to one another without a type assertion or consciously converting the type.

var a int
type myInt int
var b myInt

a = 2
b = 3
b = a         // Error! cannot use a (type int) as type myInt in assignment.
b = myInt(a)  // This is ok.

Imagine with me for a second this situation:

type MyInt int
type YourInt int

type EveryInt interface {
        addableByInt(a int) bool
}

func (i MyInt) addableByInt(a int) bool {
	// whatever logic doesn't matter
	return true
}


func (i YourInt) addableByInt(a int) bool {
	// whatever logic doesn't matter
	return true
}

func main() {
	// Two guys want to pass as an int
	b := MyInt(7)
	c := YourInt(2)
	
	// Do everything an `EveryInt` requires
	// and disguise as one 
	bi := EveryInt(b)
	ci := EveryInt(c)
	
	// Hey, look we're the same! That's the closest
    // we can get to being an int!
	bi = ci          // This is ok, we are EveryInt brotherhood
	fmt.Println(bi)  // bi is now 2
	
	// Now a real int comes along saying
	// "Hey, you two look like one of us!"
	var i int
	i = bi           // Oops! bi has been made
	
	// ci runs away at this point

}

Now back to your scenerio--imagine a *circle comes along implementing shapes:

type circle struct {
        Radius float64
}

func (c *circle) setWidth(w float64) {
        c.Radius = w
}

*circle is totally passable as shapes but it does not have Width property because it is not a *rect. An interface cannot access an underlying type's property directly, but can only do so through implemented method set. In order to access a property, a type assertion is need on the interface so that instance becomes a concrete type:

var r *rect

// Verify `s` is in fact a `*rect` under the hood
if r, ok := s.(*rect); ok {
        fmt.Println(r.Width)
}

This is at the core of why a statically-typed language like Go is always faster than dynamically-typed counterparts, which will almost always use some kind of reflection to handle type coercion dynamically for you.

答案2

得分: 3

s是shapes接口的实现者,但在for循环中没有被声明为rect类型。
如果你使用类型断言来强制将s转换为具体类型,像这样:

s.(*rect).Width

你将得到你想要的结果。

不过,你需要小心在混合使用具体类型和接口时。

英文:

s is a an implementer of the shapes interface, but in the for loop not typed as a rect.
If you do a type assertion to force s be of the concrete type like so:

s.(*rect).Width

You will get what you want.

You need to be careful about mixing concrete types and interfaces like that though.

huangapple
  • 本文由 发表于 2016年4月8日 12:36:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/36491620.html
匿名

发表评论

匿名网友

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

确定