2013-08-15 6 views
5

Dlaczego boolean zawiera All (Collection <?> c); metoda zbierania frameworku jest dozwolona dla każdego typu ?. Ale boolean addAll (Collection <? Extends E> c); pozwala na ? extends E. Tak, napisałem program do wyjaśnienia. Oto mój programDecyzja o projekcie boolean zawieraAll (Collection <?> c) vs boolean addAll (Collection <? extends E> c); w ramach kolekcji

public class ContainAllTest { 
    // take ServiceDto 
    ArrayList<ServiceDto> resultList = new ArrayList<ServiceDto>(); 

    void Test() { 

     ServiceDto serviceDto = new ServiceDto(); 
     serviceDto.setName("test"); 
     resultList.add(serviceDto); 
     // another arraylist that takes String 
     ArrayList<String> resultList1 = new ArrayList<String>(); 
     resultList1.add("test"); 
     // no error, goes for run time.Contain all checking is done for two generic type ServiceDto and String: 
     resultList.containsAll(resultList1); 
     // error shown at compile time,as addAll take ServiceDto as generic type but the generic type for resultList1 take String: 
     resultList.addAll(resultList1);  
    } 

Więc moje pytanie brzmi, kiedy mogę uzyskać przewagę resultList.containsAll (resultList1); gdy typ rodzajowy jest different.In moim przypadku String i ServiceDto.Was istnieją pewne rzeczy źle zastępując logiczną containsAll (Collection <?> c) ​​ z logiczną containsAll (Collection <? rozciąga E> c)

+0

Równe i hashCode są metodami w Object, czyli . Gdy szukasz kolekcji zawierającej coś, czego nie chcesz, musisz za każdym razem przesyłać ją do odpowiedniego typu. –

+0

@artbristol: Tak, to zasadniczo to samo pytanie, które ma lepszą odpowiedź. Będę głosować, aby oznaczyć to jako duplikat. –

+0

Byłbym żmudny, gdybym musiał skopiować zawartość mojej kolekcji do kolekcji tylko po to, abym mógł wywołać containsAll(). Jest to zasadniczo odpowiednik metody equals() akceptującej parametr typu Object. (Ok, z typem wymazania obie Kolekcje to tylko Kolekcja i mógłbym ją rzucić, ale to tylko hack i powinniśmy napisać nasz kod tak, jakby to nie działało.) –

Odpowiedz

2

ten nie daje przewagi, to uratować cpu. Generics są erased przez kompilator i zastąpione przez Casts.

Dla typu metody bezpieczeństwa należy wziąć pod uwagę bezpieczeństwo. Użytkownik powinien mieć możliwość dodania tylko Collection<E> lub jakiejś podklasy z E do Collection<E>.

Jeśli spojrzeć na kod źródłowy dla AbstractCollection widzisz tę metodę:

public boolean addAll(Collection<? extends E> c) { 
    boolean modified = false; 
    for (E e : c) 
     if (add(e)) 
      modified = true; 
    return modified; 
} 

Kiedy skompilowany będzie wyglądać (coś) jak

public boolean addAll(Collection c) { 
    boolean modified = false; 
    for (Object e : c) 
     if (add((E)e)) 
      modified = true; 
    return modified; 
} 

Tj każdy element kolekcji, który ma zostać dodany, musi zostać przeniesiony z Object do E przed dodaniem.

Dla metody containsAll nie ma znaczenia. Ponieważ metoda equals jest zdefiniowana jako equals(Object other), możesz bezpiecznie wywołać ją z dowolnym innym Collection i nie ma ryzyka, że ​​s będzie to . Unikając stosowania generycznych, kompilator może uniknąć dodawania rzutów.

+7

Nie sądzę, że to ma coś wspólnego z "cpu ticks". – assylias

+0

@assylias Casting nie jest bezpłatny. Ale ma także tę zaletę, że zapewnia lepszą specyfikację interfejsu. –

+4

Casting może nie jest darmowy, ale nie sądzę, że ma to coś wspólnego z projektem klasy. – assylias

3

Zgaduję Powodem jest to, że containsAll (i contains, remove, removeAll) wykorzystuje Object#equals dla porównania.

Można, być może, zastąpić metodę Object#equals w E, która może zwrócić true dla obiektów niezwiązanej z nią klasy. Nie żeby to był dobry pomysł, ale może to być prawidłowa implementacja.

+1

Czasami jest to dobry pomysł. Na przykład 'java.util.List' wymaga, aby' .equals() 'zwracało wartość true dla list o tej samej zawartości, nawet dla różnych klas list. – newacct

1

Powód jest taki sam jak w przypadku metod add i contains.

pobiera argument typowy dla kolekcji, ponieważ do kolekcji można dodać tylko takie obiekty. Właśnie o to chodzi w przypadku używania generyków w kolekcjach.

contains (a także inne metody z zakresu zbierania, takie jak remove i Map.get) akceptuje dowolny obiekt jako parametr. Istnieją co najmniej dwa powody.

Po pierwsze, jak powiedział Tobias Brandt, mogą istnieć obiekty całkowicie oddzielnego typu, które są "równe" (zgodnie z ich implementacją equals) do obiektu w kolekcji.

Po drugie, każdy zbiór Collection<E> mogą być postrzegane jako Collection<? extends D> gdzie D jest super klasa E, lub nawet jako Collection<?> (który jest taki sam jak Collection<? extends Object>). Jeśli zrobisz to upcast, nie możesz już wywoływać metody add, ponieważ jej sygnatura będzie wyglądać jak add(?), a kompilator zabrania wywoływania jej, ponieważ nigdy nie zapewniłoby to bezpieczeństwa typu (i dobrze, że nie możesz zadzwonić pod numer add, ponieważ możesz dodaj niewłaściwe typy do kolekcji). Jednak nadal może być przydatne wywoływanie contains i jest to zawsze bezpieczne, więc dlaczego nie powinno być dozwolone? Aby to umożliwić, metoda contains musi mieć jako typ parametru Object, w przeciwnym razie nie może być nazwana podobnie do add.

Podpisy pod numerami addAll i containsAll są zgodne z tą samą zasadą.

+1

"Jednak nadal może być użyteczne wywoływanie zawiera, a to jest zawsze bezpieczne typu" Ale to znowu dlatego, że 'Object.equals()' przyjmuje wszystkie typy. Jeśli 'Object.equals()' nie weźmie wszystkich typów, to nie zawsze będzie bezpiecznym typem. – newacct

Powiązane problemy