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

时间:2016-04-08 04:36:23

标签: go

我正在尝试更好地理解界面,并且我不理解为什么s没有字段Width。我的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???
    }
}

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

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

2 个答案:

答案 0 :(得分:4)

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

}

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

type circle struct {
        Radius float64
}

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

*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)
}

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

答案 1 :(得分:2)

是一个形状接口的实现者,但在for循环中没有输入为rect。 如果你做一个类型断言来强制s具有如下具体类型:

s.(*rect).Width

你会得到你想要的东西。

尽管如此,你需要注意混合混凝土类型和接口。