SWE/CS 332 In Class Exercise # 18

Name(s):
Consider Bloch's InstrumentedHashSet, ForwardingSet, and InstrumentedSet examples:
// Broken:  Inheritance violates encapsulation!
// Approach is not general; 1) limited to specific class, 2) only works for mutable types
public class InstrumentedHashSet<E> extends HashSet<E>{ 
   private int addCount = 0;	
   public InstrumentedHashSet() {}

   @Override public boolean add(E e){ 
      addCount++; 
      return super.add(e); 
   }
   @Override public boolean addAll(Collection<? extends E> c){ 
       // What to do with addCount?
       return super.addAll(c); 
   }
   public int getAddCount(){ return addCount; }
}



// Re-usable wrapper uses composition instead of inheritance // Note power of the implemented interface public class ForwardingSet<E> implements Set<E> { private final Set<E> s; public ForwardingSet(Set<E> s){ this.s = s; } @Override public boolean add(E e) { return s.add(e); } @Override public boolean remove(Object o){ return s.remove(o); } @Override public boolean equals(Object o){ return s.equals(o); } @Override public int hashCode() { return s.hashCode(); } @Override public String toString() { return s.toString(); } // Other forwarded methods from Set interface omitted }

// Implementation details in underlying set are encapsulated again 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; } }

Consider also the following client code:
   Set<String> r = new HashSet<String>();
   r.add("ant"); r.add("bee");

   Set<String> sh = new InstrumentedHashSet<String>();
   sh.addAll(r);

   Set<String> s =  new InstrumentedSet<String>(r);
   s.add("ant"); s.add("cat");

   Set<String> t = new InstrumentedSet<String>(s);
   t.add("dog");

   r.remove("bee"); s.remove("ant");
  1. How do you think the addCount variable should be updated in the addAll() method in InstrumentedHashSet?


    • Why is this an unpleasant question? Is the answer documented anywhere? Should it be?


    • What does the answer say about inheritance?


    • Does equals() behave correctly in InstrumentedHashSet?


  2. Given your previous answer, what is the value of sh.addCount at the end of the computation?


  3. Consider the InstrumentedSet solution. Besides being correct (always a plus!) why is it more general than the InstrumentedHashSet solution?


  4. At the end of the computation, what are the values of: r, s, and t?


  5. What would a call to s.getAddCount() return at the end of the computation?


  6. At the end of the computation, what are the values of: r.equals(s), s.equals(t), and t.equals(s)?

    • Are there any problems with the equals() contract?

  7. Would this still work if you globally replaced sets with lists?


  8. Would this still work if you globally replaced sets with collections?

Note: There is a lot going on in this example. I highly recommend that you play with the code until you understand it.