请考虑以下代码:
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的最后一个语句让我回到了底层结构但是我需要导出这个结构来重新获得它。
如果我在库中使用了一个包,其中导出的唯一符号是接口(大I,我们上面的例子中第5-20行之间的小a),那么我无法将接口转换为原始类型我在另一个包或文件中。
因为原始结构存储在接口中,是否有一种简单的方法来获取它的引用并使用尚未在接口中声明的方法并仅附加到结构。
答案 0 :(得分:4)
是的,在一般情况下,您需要type assertion(或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()
方法。我们有一个类型f
的变量Foo
,它存储的具体类型是*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上尝试)。
天哪,你甚至可以延长" Foo
使用其他方法,并键入断言"扩展"接口:
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
答案 1 :(得分:2)
是。键入Assertion是在处理接口时返回结构指针的唯一方法。