1

如果我们考虑使用以下类来计算 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)调用。但是为什么叫我重新定义的?(这就是为什么我得到 15 而不是 10)super.addAll(c)hashSetaddAll(c)add(e)super.addAll(c)add method

4

2 回答 2

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()并不意味着该对象突然处于“非覆盖”模式或类似的状态。

于 2013-05-07T19:17:11.930 回答
1

这就是多态性的工作原理。您的对象是 type CountingHashSet,因此调用add将调用CountingHashSet.add,即使来自超类型。

于 2013-05-07T19:16:16.080 回答