类型断言是处理接口时返回结构指针的唯一方法吗?

Is Type Assertion the only way to get back at the struct pointer when dealing with interfaces?

考虑以下代码:

package main

import "fmt"
// 5
type I interface {
    Foo() string
}

type a struct {
    i int
}

func (s a) Foo() string {
    return "We are here!"
}

func (s a) Bar() string {
    return "Major Tom!"
}
// 20
func main() {
    var x I = &a{i: 42}
    fmt.Println(x.Foo())
    fmt.Println(x.(*a).Bar())
}

main 的最后一条语句返回了底层结构,但我需要导出该结构才能返回。

如果我在库中使用一个包,其中唯一导出的符号是接口(我们上面示例中第 5-20 行之间的大 I,小 a),那么我无法将接口转换为原始接口当我在另一个包或文件中时键入。

因为原始结构存储在接口中,是否有一种简单的方法可以取回其引用并使用未在接口中声明且仅附加到结构的方法。

是的。类型断言是处理接口时返回结构指针的唯一方法。

是的,在一般情况下,您需要 type assertion (or a type switch) 来取回接口值中的包装值。

但是您不需要从接口取回存储的具体值来调用它拥有的其他方法(并且不属于接口类型的一部分)。

您可以从一个接口值类型断言另一个接口值,另一个接口值的类型包含您确实要调用的方法。

看这个例子:

type Foo interface {
    Bar() string
}

type fooImpl struct{}

func (fooImpl) Bar() string { return "Bar from fooImpl" }

func (fooImpl) Baz() string { return "Baz from fooImpl" }

func main() {
    var f Foo = &fooImpl{}

    if x, ok := f.(interface{ Baz() string }); ok {
        fmt.Println(x.Baz())
    } else {
        fmt.Println("f does not have a Baz() method!")
    }
}

Foo 只有一个 Bar() 方法。我们有一个类型为Foo的变量f,它存储的具体类型是*fooImpl,它还有另外一个方法:fooImpl.Baz().

所以我们可以从它类型断言类型 interface{ Baz() string } 的值,然后简单地调用 Baz() 结果。

上面的输出是(在Go Playground上试试):

Baz from fooImpl

从另一个接口值对接口进行类型断言不需要导出包装值的类型。

您还可以为您键入断言的接口类型创建一个新类型,匿名类型不是必需的:

type MyFoo interface{ Baz() string }

if x, ok := f.(MyFoo); ok {
    fmt.Println(x.Baz())
} else {
    fmt.Println("f does not have a Baz() method!")
}

输出相同(在 Go Playground 上尝试)。

地狱,你甚至可以 "extend" Foo 使用其他方法,并键入断言 "extended" 接口:

type MyFoo interface {
    Foo
    Baz() string
}

if x, ok := f.(MyFoo); ok {
    fmt.Println(x.Bar())
    fmt.Println(x.Baz())
} else {
    fmt.Println("f does not have a Baz() method!")
}

现在,在此示例中,x 既是 Foo 又是具有 Baz() 方法的值。输出(在 Go Playground 上尝试):

Bar from fooImpl
Baz from fooImpl