Go 为什么可以';我不能在接口中访问此字段吗?

Go 为什么可以';我不能在接口中访问此字段吗?,go,Go,我试图更好地理解接口,但不理解为什么s没有字段Width。我的: 为什么r有宽度,而s没有宽度?我得到的确切错误是: s.Width undefined (type shapes has no field or method Width) shapes接口是*rect实现的,但它不是一种具体类型*rect。就像任何界面一样,它是一套方法,允许任何满足它的类型通过,就像给它一个临时访客贴纸,让它爬上大楼 例如,如果有一只猴子(或者是一只海豚),它能做任何人类能做的事情,在Go的大楼里,它能通过警

我试图更好地理解接口,但不理解为什么
s
没有字段
Width
。我的:

为什么
r
有宽度,而
s
没有宽度?我得到的确切错误是:

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

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

例如,如果有一只猴子(或者是一只海豚),它能做任何人类能做的事情,在Go的大楼里,它能通过警卫,上电梯。然而,这并不意味着他在基因上是人类

Go是静态类型的,这意味着即使是具有相同基础类型的两个类型,如果没有类型断言或有意识地转换类型,也无法动态转换或强制转换为另一个类型

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.
让我想象一下这种情况:

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

}
现在回到您的场景——想象一个
*圆
来实现
形状

type circle struct {
        Radius float64
}

func (c *circle) setWidth(w float64) {
        c.Radius = w
}
*圆圈
完全可以作为
形状
使用,但它没有
宽度
属性,因为它不是
*矩形
。接口不能直接访问基础类型的属性,但只能通过实现的方法集访问。为了访问属性,接口上需要一个类型断言,以便实例成为具体类型:

var r *rect

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

这就是为什么像Go这样的静态类型语言总是比动态类型语言快的核心原因,动态类型语言几乎总是使用某种反射来为您动态地处理类型强制。

s是shapes接口的实现者,但在for循环中不是作为rect类型的。 如果执行类型断言以强制s为具体类型,如下所示:

s.(*rect).Width
你会得到你想要的


但是,在混合具体类型和接口时需要小心。

离题:一种好的风格是对类/结构名称使用单数形式,直到它成为集合/集合。所以在你的例子中,一个
shape
而不是
shapes
会更合适。你能详细说明一下“你需要小心混合像那样的混凝土类型和接口吗?”?那么,有更好的方法吗?例如,在您的示例中,为什么您可以假设
映射[string]形状的这个特定元素是
*rect
。您是否应该在界面中使用另一个函数来公开所需内容?如果您有其他实现(如pie-o-pah圆),您应该怎么做?那么为什么我可以访问r.Width?@kristen,因为
r
已经是一个具体类型(即
*rect
)。Go有一个静态类型,这意味着即使两个共享相同底层类型的类型在没有类型断言或我们有意识地定义正确类型的情况下也不能相互转换或强制。接口不是具体的类型。这是一组方法,简单地说“如果这个人能做到所有这些,她会去这个类型能去的任何地方”,但它不会让她成为这个类型,直到你“要求她的id”(又名类型断言)。
s.(*rect).Width