2013-08-29 3 views
2

Я читаю книгу, эффективную java от Джошуа Блоха. в пункте 16 «предпочтения композиции над наследованием» он приводит пример использования HashSet и запрос количества добавленных элементов с момента его создания (не путать с текущим размером, который уменьшается при удалении элемента). он предоставил следующий код, и здесь getAddCount возвращает 6, что я могу понять. Это должно фактически вернуть 3. (Это потому, что метод addAll HashSet осуществляаются на вершине своего метода добавления)Как выглядит класс обертки, как описано Джошуа Блох?

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()); 
     } 
    } 

, как это работает? В основном методе я создаю экземпляр HashSet и используя метод addAll, я добавляю все элементы списка. но HashSet вызывает его метод addAll (который, в свою очередь, использует свой метод добавления), которая должна быть такой же, как и в первом в правильном примере, и я должен получить значение 6, но это дает мне 3.

+0

Почему вы должны получать шесть? Вы добавляете три элемента в «InstrumentedSet», который затем добавляет его «addCount» три раза соответственно. – Surveon

ответ

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() который задерживает к вашему @Override реализация add() из-за полиморфизма

@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 instance 
+1

В первом случае возвращает 6 не 3. –

+0

@LuiggiMendoza О, из-за 'add()' –

+0

доброжелательно вставьте код и запустите его и посмотрите. вы получите 6 для первого случая. Это объясняется в эффективной java Джошуа Блох. Это связано с тем, что addcount увеличивается в методах add и addAll, но методы addAll внутренне реализуют метод добавления. – eagertoLearn

1

InstrumentedSet#getAddCount() возвращает 6, так как размер массива (3) добавляют в два раза!

//InstrumentedSet 
public boolean addAll(Collection<? extends E> c) { 
    addCount += c.size(); //here 
    return super.addAll(c); //and here! 
} 

super.addAll(c); вызывает add() метод.

Более подробно:

InstrumentedSet # addAll -> ForwardingSet # addAll (из super.addAll) ->HashSet#addAll() (потому что это то, что вы даете его в основной) -> InstrumentedSet # добавить (из-за полиморфизма)

Если вы хотите исправить: удалить addCount += c.size();

InstrumentedSet#addAll возвращает 3, поскольку он называет это:

InstrumentedSet # addAll() (добавляет 3) -> ForwardingSet # addAll (из-за супер) -> HashSet # addAll (потому что forwardingset имеет поле типа HashSet) -> HashSet # add

+0

InstrumentedSet возвращает 3. но возвращает InstrumentedHashSet 6.I знаю, почему это 6.Я хочу знать, как на самом деле помогает класс-оболочка (ForwardingSet) – eagertoLearn

+0

. Я отредактировал свой ответ, чтобы сделать его более понятным. –

+0

Что означает «помогает здесь» –