本文共 1644 字,大约阅读时间需要 5 分钟。
GO语言有一个独门秘技:interface,是大师们对OOP的经典诠释,是对传统OOP思维的一个巧妙颠覆。
既优雅地实现运行时多态,又不需要像C++那样显式申明,完美的解除了类型实现跟接口调用之间的耦合。
Go语言的主要设计者之一曾经说过,如果只能选择一个Go语言的特性移植到其他语言中,他会选择接口。可见接口在GO中的地位,及其对GO这门语言所带来的活力。
Russ Cos: interface
: go interface
然而,由于GO的interface定义与具体实现类型之间不需要显式申明,只需要实现类实现interface定义的所有method即可赋值。
然而这种宽松的约定,却带来一些逻辑上的麻烦。
这里有个例子:
有两个包filepath和pet都定义了一个Walker接口,并实现了相应的实现类型。
但是巧的是这两个Walker接口的定义都是一样的
type Walker interface { Walk()}
但是显然,pet.Walker是想实现所有pet的“走路”行为
而filepath.Walker是想实现目录的“遍历”功能
然而,虽然两个接口的定义是一样的,但是期望的行为应该是不一样的。
从逻辑上说,用filepath.Walker调用pet.Dog.Walk()应该是一个错误的行为,然而从GO的语法上看,这种操作居然是合法的:
var petWalker pet.Walkervar filepathWalker filepath.Walkerprintln("call by pet.Walker:")petWalker = &pet.Dog{}petWalker.Walk()petWalker = &pet.Cat{}petWalker.Walk()petWalker = &filepath.FilePath{}petWalker.Walk()println("\ncall by filepath.Walker:")filepathWalker = &pet.Dog{}filepathWalker.Walk()filepathWalker = &pet.Cat{}filepathWalker.Walk()filepathWalker = &filepath.FilePath{}filepathWalker.Walk()println("\nThe strange is that pet.Warker and filepath.Walker has the same signiture but they are not the same one.")println("But Go treat them as the same one.")
从语法上修复这个bug的方法如下:
implements pet.Walker{ *pet.Dog *pet.Cat}implements *filepath.FilePath{ filepath.Walker filepath.Reader}
通过显式的申明 实现并可以使用接口的具体类型,告诉编译器什么样的接口赋值,是被允许的。
这种外部申明的语法,仍然没有破坏GO interface非侵入式的设计,无须对interface和实现类型做任何修改。
这样,下面的接口赋值将被编译器判断为非法
filepathWalker = &pet.Dog{}
另外,这种显式申明还有一个好处,就是可以明确通过查找引用的方法,找出实现了某个接口的所有实现类型。
起到一个建立接口定义与实现类型关系的书面说明,
不会像现在一样,通过interface名字,只能找到interface的对象,却没法找出所有实现并赋值给该接口的所有具体类型的信息。
参考链接:
Russ Coss: interface
: go interface
confused interface:
转载地址:http://yjjl.baihongyu.com/