Composition over Inheritance

Inheritance breaks autonomy encapsulation. Changes to superclass’s may break the subclass. So subclasses are updated with their parent class.

For example, a class that inherits HashSet,

public class InstrumentedHashSet<E> extends HashSet<E> {
  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() {
    returnaddCount; }}Copy the code

If I call addAll and pass in a List of size = 3,

InstrumentedHashSet<String> s =
new InstrumentedHashSet<String>();
s.addAll(Arrays.asList("Snap"."Crackle"."Pop"));
Copy the code

Then I call getAddCount, and I get 6 instead of 3. Explanation:

Inside a HashSet, the addAll method is implemented based on the Add method, even if the HashSet documentation does not specify this detail, which is reasonable. So the addAll method in the InstrumentedHashSet first increments the addCount by 3 and then calls the addAll implementation of the HashSet with super.addall (). In this implementation, was InstrumentedHashSet covered calls the add method, each element call once, the three again adds 1 to addCount respectively, so the total increased by 6

Order of execution: addAll–> addAll–> Add

Correct posture: Use composite, Wrapper class

//Wrapper class - use composition in place of inheritance
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()); }}Copy the code

//Reusable forwarding class

import java.util.*;

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)   { returns.remove(o); } public boolean containsAll(Collection<? > c) {return s.containsAll(c); }
    public boolean addAll(Collection<? extends E> c)
                                   { returns.addAll(c); } public boolean removeAll(Collection<? > c) {returns.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() { returns.toString(); }}Copy the code

In this case, the execution order is: InstrumentedSet addAll–>ForwardingSet addAll–> Set addAll–>ForwardingSet addAll–> Set addAll–> Set addAll–> Set addAll–> Set addAll–> Set addAll–> Set addAll–> Set addAll–> Set addAll–> Set addAll–> Set addAll–> Set addAll InstrumentedSet no longer calls addAll. Therefore, the result of S. geta addCount is 3. However, THIS process is my guess, I have no debug to follow, and I need to go through it for verification when I have time.


Another area of insecurity is that the methods of the parent class may change.

It is safer not to override the function. However, if the parent class updates a method with the same signature and a different return value, the subclass will not compile.

When using inheritance, be sure to ask yourself if the parent of A subclass is IS-A, and if every subclass is A parent.

reference: http://www.jianshu.com/p/4fd0345054cc http://blog.csdn.net/democreen/article/details/50158485