icz*_*cza 294
当您尝试将具体类型分配或传递(或转换)为接口类型时,会出现此编译时错误; 并且类型本身不实现接口,只有指向该类型的指针.
我们来看一个例子:
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
Run Code Online (Sandbox Code Playgroud)
该Stringer接口类型只有一个方法:String().存储在接口值中的任何值都Stringer必须具有此方法.我们还创建了一个MyType,我们MyType.String()用指针接收器创建了一个方法.这意味着该String()方法属于该类型的方法集*MyType,但不属于该方法集MyType.
当我们尝试为MyType类型变量赋值时Stringer,我们会得到有问题的错误:
m := MyType{value: "something"}
var s Stringer
s = m // cannot use m (type MyType) as type Stringer in assignment:
// MyType does not implement Stringer (String method has pointer receiver)
Run Code Online (Sandbox Code Playgroud)
但是如果我们尝试将类型的值赋值*MyType为Stringer:
s = &m
fmt.Println(s)
Run Code Online (Sandbox Code Playgroud)
我们得到了预期的结果(在Go Playground上尝试):
something
Run Code Online (Sandbox Code Playgroud)
因此需要获得此编译时错误:
解决问题的可能性:
使用结构体和嵌入时,通常不是"你"实现接口(提供方法实现),而是嵌入你的类型struct.就像在这个例子中:
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
Run Code Online (Sandbox Code Playgroud)
再次,编译时错误,因为方法集MyType2不包含String()嵌入MyType的方法,只有方法集*MyType2,所以下面的工作(在Go Playground上试试):
var s Stringer
s = &m2
Run Code Online (Sandbox Code Playgroud)
如果我们嵌入*MyType并仅使用非指针 MyType2(在Go Playground上试试),我们也可以使它工作:
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
Run Code Online (Sandbox Code Playgroud)
此外,无论我们嵌入什么(MyType或者*MyType),如果我们使用指针*MyType2,它将始终有效(在Go Playground上尝试):
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = &m2
Run Code Online (Sandbox Code Playgroud)
规范中的相关部分(来自结构类型部分):
给定结构类型
S和命名类型T,提升的方法包含在结构的方法集中,如下所示:
- 如果
S包含匿名字段T,则方法集S和*S两者都包括带接收器的提升方法T.该方法集*S还包括具有接收器的推广方法*T.- 如果
S包含匿名字段*T,则方法集S和*S两者都包括带接收器T或的提升方法*T.
换句话说:如果我们嵌入非指针类型,非指针嵌入器的方法集只能获得具有非指针接收器的方法(来自嵌入式类型).
如果我们嵌入指针类型,非指针嵌入器的方法集将获得具有指针和非指针接收器的方法(来自嵌入式类型).
如果我们对嵌入器使用指针值,无论嵌入类型是否为指针,指向嵌入器的指针的方法集总是获得带有指针接收器和非指针接收器的方法(来自嵌入式类型).
注意:
有一个非常类似的情况,即当你有一个包装值的接口值MyType,并尝试从中键入断言另一个接口值时,Stringer.在这种情况下,由于上述原因,断言将不会成立,但我们得到的运行时错误略有不同:
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
Run Code Online (Sandbox Code Playgroud)
运行时恐慌(在Go Playground上试试):
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
Run Code Online (Sandbox Code Playgroud)
尝试转换而不是类型断言,我们得到了我们正在讨论的编译时错误:
m := MyType{value: "something"}
fmt.Println(Stringer(m))
Run Code Online (Sandbox Code Playgroud)
Sam*_*igh 21
为了简短起见,假设你有这个代码,你有一个Loader接口和一个实现这个接口的WebLoader.
package main
import "fmt"
// Loader defines a content loader
type Loader interface {
Load(src string) string
}
// WebLoader is a web content loader
type WebLoader struct{}
// Load loads the content of a page
func (w *WebLoader) Load(src string) string {
return fmt.Sprintf("I loaded this page %s", src)
}
func main() {
webLoader := WebLoader{}
loadContent(webLoader)
}
func loadContent(loader Loader) {
loader.Load("google.com")
}
Run Code Online (Sandbox Code Playgroud)
所以这段代码会给你这个编译时错误
./main.go:20:13:不能在loadContent的参数中使用webLoader(类型WebLoader)作为类型Loader:WebLoader不实现Loader(Load方法有指针接收器)
所以你只需要改变webLoader := WebLoader{}如下:
webLoader := &WebLoader{}
Run Code Online (Sandbox Code Playgroud)
那么为什么它会修复因为你定义这个函数func (w *WebLoader) Load来接受一个指针接收器.有关更多说明,请阅读@icza和@karora答案
看到这种情况发生的另一种情况是,如果我想创建一个接口,其中某些方法将修改内部值,而其他方法则不会。
type GetterSetter interface {
GetVal() int
SetVal(x int) int
}
Run Code Online (Sandbox Code Playgroud)
然后实现此接口的内容可能是:
type MyTypeA struct {
a int
}
func (m MyTypeA) GetVal() int {
return a
}
func (m *MyTypeA) SetVal(newVal int) int {
int oldVal = m.a
m.a = newVal
return oldVal
}
Run Code Online (Sandbox Code Playgroud)
因此,实现类型可能会有一些方法,这些方法是指针接收器,而有些则不是,并且由于我有各种各样的GetterSetters方法,我想在测试中检查它们是否都按预期执行。
如果我要做这样的事情:
myTypeInstance := MyType{ 7 }
... maybe some code doing other stuff ...
var f interface{} = myTypeInstance
_, ok := f.(GetterSetter)
if !ok {
t.Fail()
}
Run Code Online (Sandbox Code Playgroud)
然后,我不会收到前面提到的“ X不能实现Y(Z方法具有指针接收器)”错误(因为它是编译时错误),但是我将很难追查测试失败的确切原因。 。
相反,我必须确保使用指针进行类型检查,例如:
var f interface{} = new(&MyTypeA)
...
Run Code Online (Sandbox Code Playgroud)
要么:
myTypeInstance := MyType{ 7 }
var f interface{} = &myTypeInstance
...
Run Code Online (Sandbox Code Playgroud)
然后所有人都对测试感到满意!
可是等等!在我的代码中,也许我有一些在某个地方接受GetterSetter的方法:
func SomeStuff(g GetterSetter, x int) int {
if x > 10 {
return g.GetVal() + 1
}
return g.GetVal()
}
Run Code Online (Sandbox Code Playgroud)
如果我从另一个类型方法内部调用这些方法,则会产生错误:
func (m MyTypeA) OtherThing(x int) {
SomeStuff(m, x)
}
Run Code Online (Sandbox Code Playgroud)
以下任何一个呼叫都将起作用:
func (m *MyTypeA) OtherThing(x int) {
SomeStuff(m, x)
}
func (m MyTypeA) OtherThing(x int) {
SomeStuff(&m, x)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
47687 次 |
| 最近记录: |