简体   繁体   English

HashSet调用构造函数中的可重写方法

[英]HashSet calls overridable method in constructor

It is bad practice to call overridable methods in a constructor (see this link ). 在构造函数中调用可重写的方法是不好的做法(请参阅此链接 )。 This will always call the method defined in the class and not a derived class' method. 这将始终调用类中定义的方法,而不是派生类的方法。 In Java's HashSet there is a constructor that accepts a Collection . 在Java的HashSet有一个接受Collection的构造函数。 This method delegates to the addAll method. 此方法委托给addAll方法。 My question is why won't this break any derived classes of HashSet . 我的问题是,为什么这不会破坏HashSet任何派生类。

Constructs a new set containing the elements in the specified collection. 构造一个新集合,其中包含指定集合中的元素。 The HashMap is created with default load factor (0.75) and an initial capacity sufficient to contain the elements in the specified collection. 使用默认的加载因子(0.75)和足以容纳指定集合中的元素的初始容量创建HashMap。 Parameters: c the collection whose elements are to be placed into this set Throws: java.lang.NullPointerException if the specified collection is null 参数:c将元素放置到此集合中的集合抛出:java.lang.NullPointerException如果指定的集合为null

public HashSet(Collection<? extends E> c) {
    map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
    addAll(c);
}

The answer is that it does break subclasses, potentially. 答案是,它确实有可能破坏子类。 It's easy to show that: 很容易证明:

BrokenSet.java: BrokenSet.java:

import java.util.*;

public class BrokenSet<E> extends HashSet<E> {
    private final List<E> list = new ArrayList<E>();

    public BrokenSet(Collection<? extends E> c) {
        super(c);
    }

    @Override public boolean add(E item) {
        if (super.add(item)) {
            list.add(item);
            return true;
        }
        return false;
    }
}

Main.java: Main.java:

import java.util.*;

public class Main{

    public static void main(String[] args) {
        Collection<String> strings = Arrays.asList("x", "y");
        Set<String> set = new BrokenSet<>(strings);
    }

}

Run Main , and you'll get: 运行Main ,您将获得:

Exception in thread "main" java.lang.NullPointerException
        at BrokenSet.add(BrokenSet.java:12)
        at java.util.AbstractCollection.addAll(Unknown Source)
        at java.util.HashSet.<init>(Unknown Source)
        at BrokenSet.<init>(BrokenSet.java:7)
        at Main.main(Main.java:7)

... because while the superconstructor is running, it calls BrokenSet.add which expects list to be non-null. ...因为当超级BrokenSet.add正在运行时,它将调用BrokenSet.add ,它期望list为非null。

Subclasses would need to be written carefully to avoid this being an issue. 子类需要仔细编写,以避免出现问题。 (You might want to look at what LinkedHashSet does, for example.) (例如,您可能想要查看LinkedHashSet功能。)

As it's not the only constructor, just additional one added for convenience, then it's not that scary: you still can implement the similar constructor in the derived Set calling default superclass constructor (with no arguments), initializing your instance and then calling addAll(). 由于它不是唯一的构造函数,为方便起见仅添加了一个构造函数,所以它并不那么可怕:您仍然可以在派生Set中实现类似的构造函数,调用默认超类构造函数(不带参数),初始化实例,然后调用addAll() 。 Probably it's implemented that way because it was easier to do so. 可能是这样实现的,因为这样做比较容易。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM