接口类型的 Golang String 方法

edu*_*eon 3 string methods interface go

我正在解决《Go 编程语言》一书的问题,在练习 7.13 中需要String向接口添加一个方法。

是否可以String()向接口添加方法?因为直到运行时才知道数据类型。

icz*_*cza 5

向接口添加方法只是意味着将该方法包含在接口类型定义的方法集中。

例如这是一个接口:

type Fooer interface {
    Foo()
}
Run Code Online (Sandbox Code Playgroud)

它有一种方法:Foo(). 要将String()方法添加到此接口:

type Fooer interface {
    Foo()
    String() string
}
Run Code Online (Sandbox Code Playgroud)

完毕。我们已将该String()方法添加到该接口中。

这样做的结果是,如果一个具体类型想要实现这个Fooer接口,以前只需一个Foo()方法就足够了。现在它还必须有一个String()方法。

另请注意,在 Go 中实现接口是隐式的。没有意向声明。如果类型具有接口的所有方法,则它隐式满足接口。

具体类型可能有也可能没有String()方法,独立于我们的Fooer接口。具体类型甚至可能不知道我们的Fooer接口,而且也不需要。

这是实现该接口的具体类型Fooer

type fooerImpl int

func (f fooerImpl) Foo() {
    fmt.Printf("Foo() called, I'm %d\n", int(f))
}

func (f fooerImpl) String() string {
    return fmt.Sprintf("Foo[%d]", int(f))
}
Run Code Online (Sandbox Code Playgroud)

测试它:

var f Fooer = fooerImpl(3)
f.Foo()
fmt.Println(f.String())
fmt.Println(f)
Run Code Online (Sandbox Code Playgroud)

输出(在Go Playground上尝试):

Foo() called, I'm 3
Foo[3]
Foo[3]
Run Code Online (Sandbox Code Playgroud)

向接口添加方法的后果

如果随意向接口添加新方法,可能会导致某些先前实现该接口的现有具体类型不再实现该接口(由于缺少新添加的方法)。

如果这些现有的具体类型用作接口的实例,它们将导致编译时错误,并且在您添加缺少的新方法之前不会起作用。例如,如果我们fooerImpl.String()从上面的示例中删除该方法,则代码var f Fooer = fooerImpl(3)会导致编译时错误:

cannot use fooerImpl(3) (type fooerImpl) as type Fooer in assignment:
    fooerImpl does not implement Fooer (missing String method)
Run Code Online (Sandbox Code Playgroud)

如果现有的具体类型不直接用作接口的实例,而是例如将它们包装在空接口中传递interface{},并且使用类型断言来提取接口的值,则这些类型断言将不再有效。类型断言的“简单”形式x.(T)将导致运行时恐慌,特殊形式v, ok := x.(T)将返回nilfalse

例如,使用上面的fooerImpl类型而不String()使用方法:

var f0 interface{} = fooerImpl(3)
f := f0.(Fooer)
Run Code Online (Sandbox Code Playgroud)

导致运行时恐慌:

panic: interface conversion: main.fooerImpl is not main.Fooer: missing method String
Run Code Online (Sandbox Code Playgroud)

并使用类型转换的特殊形式:

if f, ok := f0.(Fooer); ok {
    f.Foo()
} else {
    fmt.Println("Not instance of Fooer!", f)
}
Run Code Online (Sandbox Code Playgroud)

结果是:

Not instance of Fooer! <nil>
Run Code Online (Sandbox Code Playgroud)

如果现有的具体类型已经有一个您刚刚添加到接口中的方法,并且它具有相同的签名(相同的参数和返回类型),那么这很酷,具体类型将“继续”实现新接口也。如果签名不匹配(例如现有方法具有不同的返回类型),则它将不满足接口,并且与没有同名方法相同。