在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
类型转换成空接口类型的值,就像这样:spa
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
接口,请参看后面两节。code