Go开发工程师
未来3-5年企业高性能项目不可替代的语言,从基础到项目实战再到重构,真正从入门到精通
在Go语言中,一个接口类型总是代表着某一种类型(即所有实现它的类型)的行为。一个接口类型的声明通常会包含关键字type
、类型名称、关键字interface
以及由花括号包裹的若干方法声明。示例如下:
type Animal interface { Grow() Move(string) string }
注意,接口类型中的方法声明是普通的方法声明的简化形式。它们只包括方法名称、参数声明列表和结果声明列表。其中的参数的名称和结果的名称都可以被省略。不过,出于文档化的目的,我还是建议大家在这里写上它们。因此,Move
方法的声明至少应该是这样的:
Move(new string) (old string)
如果一个数据类型所拥有的方法集合中包含了某一个接口类型中的所有方法声明的实现,那么就可以说这个数据类型实现了那个接口类型。所谓实现一个接口中的方法是指,具有与该方法相同的声明并且添加了实现部分(由花括号包裹的若干条语句)。相同的方法声明意味着完全一致的名称、参数类型列表和结果类型列表。其中,参数类型列表即为参数声明列表中除去参数名称的部分。一致的参数类型列表意味着其长度以及顺序的完全相同。对于结果类型列表也是如此。
例如,如果你正确地完成了上一小节的练习的话,*Person
类型(注意,不是Person
类型)就会拥有一个Move
方法。该方法会是Animal
接口的Move
方法的一个实现。再加上我们在之前为它编写的那个Grow
方法,*Person
类型就可以被看做是Animal
接口的一个实现类型了。
你可能已经意识到,我们无需在一个数据类型中声明它实现了哪个接口。只要满足了“方法集合为其超集”的条件,就建立了“实现”关系。这是典型的无侵入式的接口实现方法。
好了,现在我们已经认为*Person
类型实现了Animal
接口。但是Go语言编译器是否也这样认为呢?这显然需要一种显式的判定方法。在Go语言中,这种判定可以用类型断言来实现。不过,在这里,我们是不能在一个非接口类型的值上应用类型断言来判定它是否属于某一个接口类型的。我们必须先把前者转换成空接口类型的值。这又涉及到了Go语言的类型转换。
Go语言的类型转换规则定义了是否能够以及怎样可以把一个类型的值转换另一个类型的值。另一方面,所谓空接口类型即是不包含任何方法声明的接口类型,用interface{}
表示,常简称为空接口。正因为空接口的定义,Go语言中的包含预定义的任何数据类型都可以被看做是空接口的实现。我们可以直接使用类型转换表达式把一个*Person
类型转换成空接口类型的值,就像这样:
p := Person{"Robert", "Male", 33, "Beijing"} v := interface{}(&p)
请注意第二行。在类型字面量后跟由圆括号包裹的值(或能够代表它的变量、常量或表达式)就构成了一个类型转换表达式,意为将后者转换为前者类型的值。在这里,我们把表达式&p
的求值结果转换成了一个空接口类型的值,并由变量v
代表。注意,表达式&p
(&
是取址操作符)的求值结果是一个*Person
类型的值,即p
的指针。
在这之后,我们就可以在v
上应用类型断言了,即:
h, ok := v.(Animal)
类型断言表达式v.(Animal)
的求值结果可以有两个。第一个结果是被转换后的那个目标类型(这里是Animal
)的值,而第二个结果则是转换操作成功与否的标志。显然,ok
代表了一个bool
类型的值。它也是这里判定实现关系的重要依据。
至此,我们掌握了接口类型、实现类型以及实现关系判定的重要知识和技巧。关于Go语言的类型转换规则的更多细节请参看Go语言规范或《Go并发编程实战》中的相关内容。而至于为什么只有*Person
类型才实现了Animal
接口,请参看后面两节。
在源码文件的第10行处加入若干代码,使该文件不出现任何编译错误,并且运行该文件会使标准输出上出现true, &{Little C 2 In the house}
。
在第10行加入的代码可以是:
type Cat struct { Name string Age uint8 Location string } func (cat *Cat) Grow() { cat.Age++ } func (cat *Cat) Move(new string) string { old := cat.Location cat.Location = new return old }
注意,答案不是唯一的。
请验证,完成请求
由于请求次数过多,请先验证,完成再次请求
打开微信扫码自动绑定
绑定后可得到
使用 Ctrl+D 可将课程添加到书签
举报