Introduction
最近总结Thinking in Java 读书笔记时,涉及到下面这段简单代码:
1 | Collection<String> list = new ArrayList<String>(); |
上面这段代码的运行结果如下:
1 | [tomorrow, hi, yes] |
进而引出了两个问题:
Collection是一个接口,其内部并没有toString方法,为什么声明为Collection类型的list对象可以调用toString方法?
list对象的toString方法明显被重写过了,重写toString方法是在哪里实现的?
Solution
首先解决第二个问题:
ArrayList的继承关系是 ArrayList -> AbstractList -> AbstractCollection。而AbstractCollection中重写了toString方法,具体实现如下:
1 | public String toString() { |
然而这个解答并不能解决全部问题,就算是ArrayList中的toString方法已经通过继承父类得到重写,ArrayList创建出的对象被向上转型赋值给了Collection类型的list引用,于是又绕回到第一个问题了,Collection接口源码中没有toString方法,为何list对象可以调用toString方法?
下面这张图通过Eclipse的自动补全工具给我们提供了一点线索:
可以看出,其实list调用的是Object中的toString方法。实际上Collection中有toString方法,只不过是隐式添加的。一个没有任何父接口的接口,如果没有显式声明toString等Object类中的public实例方法,则会被隐式添加这些方法对应的抽象方法。
简而言之,所有接口都会有对应于Object类中public实例方法的抽象方法。也就是说所有接口都会有toString, hashCode, wait, notify, getClass, equals等方法。Java Language Specification 中的原话如下:
1 | If an interface has no direct superinterfaces, then the interface implicitly declares a |
总结一下整个过程,Collection中没有显式声明toString方法,因此会隐式添加对应于Object类中toString的同名抽象方法,System.out.println(list)
list调用Collection接口中的toString方法,list实际类型为ArrayList, 由于动态绑定(此处涉及多态),调用的是ArrayList实例对象中的toString方法,ArrayList中并没有重写toString方法,又因为ArrayList的超父类AbstractCollection重写了toString方法,因此最后调用的是AbstractCollection 中的toString方法。从而实现了打印Collection所有元素的最终效果。
由此进行一下拓展,如果接口中显式声明了toString等方法又会怎样呢?此时,实现该接口的类不会被强制要求实现toString方法,原因很简单,所有的类都已经通过继承Object类实现了toString方法。因此,在平时coding的时候,如果想在接口中定义有效的强制被实现的抽象方法,最好避免与Object类中public实例方法重名。
Reference
http://stackoverflow.com/questions/12124163/do-interfaces-have-tostring-method