接口定义
接口也是一种数据类型,它代表一组方法的集合。
接口是非侵入式的。即接口设计者无需知道接口被哪些类型实现,而接口使用者只需知道实现怎样的接口,并且无须指明实现哪一个接口。编译器在编译时就会知道哪个类型实现哪个接口,或者接口该由谁来实现。
具体实现
直接来看例子。首先接口方法的功能必须通过结构体来实现。例子如下
package main
import "fmt"
type action interface {
walk(name string)
}
type sinner struct {
name string
}
func setName(s *sinner, name string) sinner {
s.name = name
return *s
}
func (s *sinner) walk(name string) {
fmt.Printf("%v is walking\n", s.name)
}
func walking(a action, name string) {
a.walk(name)
}
func main() {
var (
tmp sinner
)
setName(&tmp, "Ishmael")
walking(&tmp, tmp.name)
}
运行结果为
Ishmael is walking
可以看到我们并没有直接调用walk()方法,而是通过walking()函数去调用walk()函数。
在主函数中,将sinner结构体与action接口绑定的是这一步。我们将sinner结构体实例化(给结构里的name赋值"Ishmael",然后生成实例化变量tmp。再将tmp的地址传入到walking()函数中。此时,由于walking()函数中变量a的类型为action,而我们传入的变量tmp类型为(*sinner),结构体sinner便与接口action绑定在了一起。
walking(&tmp, tmp.name)
“鸭子类型”(Duck Typing)。
只要走起来像鸭子,或者游泳姿势像鸭子,或者叫声像鸭子,那么它就是一只鸭子。用官方术语来解释:鸭子类型只关注事物的外部行为而非内部结构。
我们将接口与结构体的绑定过程通过函数实现。只要我们传入结构体的实例化变量,函数就能自动执行接口方法。这样就不需要每次当我们要去调用一个接口时,都要去新建一个接口变量绑定对应的结构体实例化变量,再通过其调用接口。
不过需要注意的是,如果接口包含多个方法,那么结构体必须要为每个方法实现具体功能,不然会出现报错。我们试着添加向action接口中添加一个run()方法。
package main
import "fmt"
type action interface {
walk(name string)
run(name string)
}
type sinner struct {
name string
}
func setName(s *sinner, name string) sinner {
s.name = name
return *s
}
func (s *sinner) walk(name string) {
fmt.Printf("%v is walking\n", s.name)
}
func walking(a action, name string) {
a.walk(name)
fmt.Printf("%T", a)
}
func main() {
var (
tmp sinner
)
setName(&tmp, "Ishmael")
walking(&tmp, tmp.name)
}
运行结果为,可以看到会提示异常。
# command-line-arguments
.\cpt.1.go:29:10: cannot use &tmp (value of type *sinner) as action value in argument to walking: *sinner does not implement action (missing method run)编译完成,并显示退出代码 1
多态
同一结构体的不同实例化可以使用同一个接口,例子如下
package main
import "fmt"
type action interface {
walk(name string)
}
type sinner struct {
name string
}
func setName(s *sinner, name string) sinner {
s.name = name
return *s
}
func (s *sinner) walk(name string) {
fmt.Printf("%v is walking\n", s.name)
}
func walking(a action, name string) {
a.walk(name)
}
func main() {
var (
ishmael sinner
faust sinner
)
setName(&ishmael, "Ishmael")
setName(&faust, "Faust")
walking(&ishmael, ishmael.name)
walking(&faust, faust.name)
}
运行结果如下
Ishmael is walking
Faust is walking
不同结构体的实例化也能调用同一接口。我们修改一下上面的代码,将setName方法也整合进接口,再新建一个结构enemy。例子如下
package main
import "fmt"
type action interface {
walk(name string)
setName(name string)
}
type sinner struct {
name string
}
type enemy struct {
name string
}
func (s *sinner) walk(name string) {
fmt.Printf("%v is walking\n", s.name)
}
func (s *sinner) setName(name string) {
s.name = name
}
func (s *enemy) walk(name string) {
fmt.Printf("%v is walking\n", s.name)
}
func (s *enemy) setName(name string) {
s.name = name
}
func setname(a action, name string) {
a.setName(name)
}
func walking(a action, name string) {
a.walk(name)
}
func main() {
var (
ishmael sinner
faust enemy
)
setname(&ishmael, "Ishmael")
setname(&faust, "Faust")
walking(&ishmael, ishmael.name)
walking(&faust, faust.name)
}
运行结果如下
Ishmael is walking
Faust is walking
接口嵌套
接口可以嵌套另一个接口,通过接口嵌套,接口之间能形成简单的基础关系。但接口之间不具备方法重写功能,即多个接口嵌套组成一个新的接口,每个接口的方法都是唯一的。即不能出现下面这种情况--不同接口中定义了相同的方法名,但两个方法的返回值不一样。
type relationship interface {
isMyWife(name string)int
}
type action interface {
walk(name string)
setName(name string)
relationship
isMyWife(name string)string
}
把返回类型去掉或者改为同一种就行了,程序才能把两个不同接口里但定义相同的方法,当作一个方法去执行。
type relationship interface {
isMyWife(name string)
}
type action interface {
walk(name string)
setName(name string)
relationship
isMyWife(name string)
}
综上,我们优化下程序可以得到一个嵌套了其他接口的接口,并通过函数的方式对其调用。
package main
import "fmt"
type relationship interface {
relationship_isMyWife(name string)
}
type action interface {
walk(name string)
setName(name string)
relationship
}
type sinner struct {
name string
}
func (s *sinner) walk(name string) {
fmt.Printf("%v is walking\n", s.name)
}
func (s *sinner) setName(name string) {
s.name = name
}
func (s *sinner) relationship_isMyWife(name string) {
fmt.Printf("%v is my Wife!\n", s.name)
}
func getInterface(a action, name string) {
a.setName(name)
a.walk(name)
a.relationship_isMyWife(name)
}
func main() {
var (
ishmael sinner
)
getInterface(&ishmael, "Ishmael")
}
运行结果如下
Ishmael is walking
Ishmael is my Wife!