为什么我不能在界面中访问此字段?

6 go

我试图更好地理解接口,我不明白为什么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)  // this works
    for _, s := range allShapes {
        s.setWidth(7)
        fmt.Println(s.Width) // why not???
    }
}
Run Code Online (Sandbox Code Playgroud)

为什么r有宽度但s不宽?我得到的确切错误是:

s.Width undefined (type shapes has no field or method Width)
Run Code Online (Sandbox Code Playgroud)

Pie*_*Pah 7

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.
Run Code Online (Sandbox Code Playgroud)

请跟我一起想象一下这种情况:

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

}
Run Code Online (Sandbox Code Playgroud)

现在回到您的场景 - 想象一下*circle实施shapes

type circle struct {
        Radius float64
}

func (c *circle) setWidth(w float64) {
        c.Radius = w
}
Run Code Online (Sandbox Code Playgroud)

*circle完全可以通过,shapes但它没有Width属性,因为它不是*rect. 接口无法直接访问基础类型的属性,而只能通过实现的方法集来访问。为了访问属性,接口上需要类型断言,以便实例成为具体类型:

var r *rect

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

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