我正在读Joshua Bloch撰写的有效java书。 在“赞成组合而不是继承”的第16项中,他给出了一个使用HashSet并查询自创建以来添加了多少元素的示例(不要与当前大小相混淆,当删除元素时该大小会下降)。 他提供了以下代码,这里是getAddCount返回6,我可以理解。 这实际上应该返回3。 (这是因为HashSet的addAll方法是在其add方法之上实现的)
import java.util.HashSet; public class InstrumentedHashSet<E> extends HashSet<E> { // The number of attempted element insertions private int addCount = 0; public InstrumentedHashSet() { } public InstrumentedHashSet(int initCap, float loadFactor) { super(initCap, loadFactor); } @Override public boolean add(E e) { addCount++; return super.add(e); } @Override public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } public static void main(String[] args) { InstrumentedHashSet<String> s = new InstrumentedHashSet<String>(); s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); System.out.println(s.getAddCount()); } }现在,他解释了一种解决方法,使用包装类(组合和转发)。 这是我很难理解的地方。 他提供以下两个班级
public class ForwardingSet<E> implements Set<E> { private final Set<E> s; public ForwardingSet(Set<E> s) { this.s = s; } public void clear() { s.clear(); } public boolean contains(Object o) { return s.contains(o); } public boolean isEmpty() { return s.isEmpty(); } public int size() { return s.size(); } public Iterator<E> iterator() { return s.iterator(); } public boolean add(E e) { return s.add(e); } public boolean remove(Object o) { return s.remove(o); } public boolean containsAll(Collection<?> c) { return s.containsAll(c); } public boolean addAll(Collection<? extends E> c) { return s.addAll(c); } public boolean removeAll(Collection<?> c) { return s.removeAll(c); } public boolean retainAll(Collection<?> c) { return s.retainAll(c); } public Object[] toArray() { return s.toArray(); } public <T> T[] toArray(T[] a) { return s.toArray(a); } @Override public boolean equals(Object o) { return s.equals(o); } @Override public int hashCode() { return s.hashCode(); } @Override public String toString() { return s.toString(); } }和
import java.util.*; public class InstrumentedSet<E> extends ForwardingSet<E> { private int addCount = 0; public InstrumentedSet(Set<E> s) { super(s); } @Override public boolean add(E e) { addCount++; return super.add(e); } @Override public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } public static void main(String[] args) { InstrumentedSet<String> s = new InstrumentedSet<String>( new HashSet<String>()); s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); System.out.println(s.getAddCount()); } }这是怎么回事? 在main方法中,我创建了一个HashSet实例并使用addAll方法,我添加了list的所有元素。 但是HashSet调用它的addAll方法(后者又使用它的add方法),这应该与正确示例中的第一个相同,我应该得到6的值,但这给了我3。
I am reading the book effective java by Joshua Bloch. on the item 16 of "favor composition over inheritance", he gives an example of using HashSet and querying how many elements have been added since it was created(not to be confused with current size, which goes down when an element is removed). he provided the following code and here the getAddCount return 6, which I can understand. This should return 3 actually. (this is because HashSet's addAll method is implemented on top of its add method)
import java.util.HashSet; public class InstrumentedHashSet<E> extends HashSet<E> { // The number of attempted element insertions private int addCount = 0; public InstrumentedHashSet() { } public InstrumentedHashSet(int initCap, float loadFactor) { super(initCap, loadFactor); } @Override public boolean add(E e) { addCount++; return super.add(e); } @Override public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } public static void main(String[] args) { InstrumentedHashSet<String> s = new InstrumentedHashSet<String>(); s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); System.out.println(s.getAddCount()); } }Now he explains a way to fix this, using wrapper classes (composition and forwarding). here is where I am having hard time to understand. he provides the following two classes
public class ForwardingSet<E> implements Set<E> { private final Set<E> s; public ForwardingSet(Set<E> s) { this.s = s; } public void clear() { s.clear(); } public boolean contains(Object o) { return s.contains(o); } public boolean isEmpty() { return s.isEmpty(); } public int size() { return s.size(); } public Iterator<E> iterator() { return s.iterator(); } public boolean add(E e) { return s.add(e); } public boolean remove(Object o) { return s.remove(o); } public boolean containsAll(Collection<?> c) { return s.containsAll(c); } public boolean addAll(Collection<? extends E> c) { return s.addAll(c); } public boolean removeAll(Collection<?> c) { return s.removeAll(c); } public boolean retainAll(Collection<?> c) { return s.retainAll(c); } public Object[] toArray() { return s.toArray(); } public <T> T[] toArray(T[] a) { return s.toArray(a); } @Override public boolean equals(Object o) { return s.equals(o); } @Override public int hashCode() { return s.hashCode(); } @Override public String toString() { return s.toString(); } }AND
import java.util.*; public class InstrumentedSet<E> extends ForwardingSet<E> { private int addCount = 0; public InstrumentedSet(Set<E> s) { super(s); } @Override public boolean add(E e) { addCount++; return super.add(e); } @Override public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } public static void main(String[] args) { InstrumentedSet<String> s = new InstrumentedSet<String>( new HashSet<String>()); s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); System.out.println(s.getAddCount()); } }how this works? In the main method, I create an instance of HashSet and using addAll method, I add all the elements of list. but the HashSet invokes its addAll method (which in turn uses its add method), which should be the same as in the first in correct example and I should get value of 6, however this gives me 3.
最满意答案
在
public class InstrumentedHashSet<E> extends HashSet<E> {你直接添加到HashSet因为addAll()委托给超级实现
InstrumentedHashSet<String> s = new InstrumentedHashSet<String>(); s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); System.out.println(s.getAddCount());addAll()内部调用add() ,由于多态性,它addAll()实现
@Override public boolean add(E e) { addCount++; return super.add(e); }增加计数并打印6 (3 + 1 + 1 + 1)。
在
public class InstrumentedSet<E> extends ForwardingSet<E> {你要加入
private final Set<E> s;因为addAll()委托给它,所以
public static void main(String[] args) { InstrumentedSet<String> s = new InstrumentedSet<String>( new HashSet<String>()); s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); System.out.println(s.getAddCount()); }并打印3 。 这里add()在Set<E> s上调用,而不是在你的实例上调用。
结论是,如果你继承,你需要了解副作用。 super方法调用是否在内部调用任何其他方法调用? 如果是这样,您需要采取适当的行动。
继承(从底部开始)
s.add() // s is your InstrumentedHashSet instance, because of polymorphism (inheritance), this adds to the count this.add() // this is the internal call inside the HashSet#addAll() super.addAll(...) // this calls the HashSet implementation of addAll which calls add() internally s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); // s is your InstrumentedHashSet instance组成
this.add() // this is the internal call to add() inside the Set implementation s.addAll() // s is the Set<E> instance super.addAll(...) // this calls the ForwardingSet implementation of addAll() s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); // s is your InstrumentedSet instanceIn
public class InstrumentedHashSet<E> extends HashSet<E> {you're adding directly to the HashSet because the addAll() is delegating to the super implementation
InstrumentedHashSet<String> s = new InstrumentedHashSet<String>(); s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); System.out.println(s.getAddCount());The addAll() internally calls add() which defers to your @Override implementation of add() because of polymorphism
@Override public boolean add(E e) { addCount++; return super.add(e); }that increments the count and prints 6 (3 + 1 + 1 + 1).
In
public class InstrumentedSet<E> extends ForwardingSet<E> {you are adding to
private final Set<E> s;because the addAll() is delegating to it, so
public static void main(String[] args) { InstrumentedSet<String> s = new InstrumentedSet<String>( new HashSet<String>()); s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); System.out.println(s.getAddCount()); }and prints 3. Here the add() is being called on the Set<E> s, not on your instance.
The conclusion is that if you are inheriting, you need to understand the side-effects. Do the super method calls invoke any other method calls internally? If so, you need to act appropriately.
Inheritance (start from bottom)
s.add() // s is your InstrumentedHashSet instance, because of polymorphism (inheritance), this adds to the count this.add() // this is the internal call inside the HashSet#addAll() super.addAll(...) // this calls the HashSet implementation of addAll which calls add() internally s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); // s is your InstrumentedHashSet instanceComposition
this.add() // this is the internal call to add() inside the Set implementation s.addAll() // s is the Set<E> instance super.addAll(...) // this calls the ForwardingSet implementation of addAll() s.addAll(Arrays.asList("Snap", "Crackle", "Pop")); // s is your InstrumentedSet instance更多推荐
发布评论