如果我们考虑以下类来计算在HashSet中添加的对象:
public class CountingHashSet<E> extends HashSet<E> {
public int addCount = 0;
@Override
public boolean add(E e) {
addCount +=1;
return super.add(e);
}
@Override
public boolean addAll(Collection<?
extends E> c) {
addCount += c.size();
return super.addAll(c);
}
}
然后,JUnit测试失败:
@Test
public void testCount() {
CountingHashSet<Integer> s = new CountingHashSet<>();
s.addAll(Arrays.asList(1, 2, 3, 4, 5));
for (int i = 6; i <= 10; ++i)
s.add(i);
assertEquals(10, s.addCount);
}
我得到以下内容:
java.lang.AssertionError: expected:<10> but was <15>
为什么我得到15?在我看来s.addAll(myCollection)
致电super.addAll(c)
,如果我查看hashSet
的源代码,我看到addAll(c)
调用add(e)
来添加每个元素。但为什么super.addAll(c)
会调用我重新定义的add method
? (这就是为什么我得到15而不是10)
答案 0 :(得分:5)
你将继承视为组合。不是。这些电话不会在add()
上“HashSet
” - 它们最终会在当前对象上显示“add()
”。
但为什么super.addAll(c)调用我重新定义的add方法?
因为这就是虚拟方法的行为方式。 addAll
只调用add()
,它将使用实际类型中覆盖次数最多的实现。这就是多态始终的工作方式。让我们写一个更简单的例子:
class Superclass {
public void foo() {
bar();
}
public void bar() {
System.out.println("Superclass.bar()");
}
}
class Subclass extends Superclass {
@Override
public void bar() {
System.out.println("Subclass.bar()");
}
}
public class Test {
public static void main(String [] args) {
Superclass x = new Subclass();
x.foo(); // Prints Subclass.bar()
}
}
Subclass.bar()
的结果是您对此示例的期望吗?如果是这样,您认为您的版本会有什么不同?仅仅因为你正在调用super.addAll()
并不意味着该对象突然处于“非重写”模式或类似的任何模式。
答案 1 :(得分:1)
多态性是如何工作的。您的对象属于CountingHashSet
类型,因此对add
的调用将调用CountingHashSet.add
,即使是超级类型也是如此。