2013-08-15 7 views
5

Почему boolean containsAll (Collection <?> c); метод рамок сбора допускается для каждого типа ?. Но boolean addAll (Коллекция <? Extends E> c); Разрешить ? расширяет E. Так, я написал программу для уточнения. Вот моя программаКонструктивное решение boolean containsAll (Collection <?> c) vs boolean addAll (Collection <? extends E> c); в рамках сбора

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

Итак, мой вопрос, когда я могу получить преимущество resultList.containsAll (resultList1); когда общий типа different.In моего случая строки и ServiceDto.Was есть некоторые вещи неправильно, заменяющих булева containsAll (Коллекция <?> с) с булевой containsAll (Collection <? расширяет E> с)

+0

Equals и hashCode - это методы в Object, то есть . Когда вы ищете коллекцию, содержащую что-то, что вы не хотите, чтобы каждый раз приводить ее к соответствующему типу. –

+0

@artbristol: Да, это по сути тот же вопрос, и у него есть лучший ответ. Я проголосую, чтобы отметить его как дубликат. –

+0

Было бы скучно, если бы мне пришлось скопировать содержимое моей коллекции в коллекцию , чтобы я мог вызвать containsAll(). Это по сути эквивалентно методу equals(), принимающему параметр типа Object. (Хорошо, с типом стирания, оба сборника - это всего лишь коллекция , и я мог бы использовать ее, но это всего лишь взломать, и мы должны написать наш код, как если бы он не работал.) –

ответ

2

Этих заключается не в том, чтобы дать преимущество, чтобы сохранить тики клепа. Генераторы составляют erased компилятором и заменяются листами.

Для безопасности типа addAll необходимо учитывать безопасность. Пользователю должно быть разрешено добавлять Collection<E> или некоторый подкласс E в Collection<E>.

Если вы посмотрите на исходный код для AbstractCollection вы видите этот метод:

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

При компиляции он будет выглядеть (что-то), как

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

Т.е. каждый элемент добавляемой коллекции должен быть отлит от Object до E перед добавлением.

Для метода containsAll это не имеет значения. Поскольку метод equals определяется как equals(Object other), вы можете смело называть его любым другим Collection и нет никакого риска ClassCastException s. Избегая использования дженериков, компилятор может избежать добавления в приведениях.

+7

Я не думаю, что это что-то делать с «cpu ticks». – assylias

+0

@assylias Кастинг не является бесплатным. Но это также имеет преимущество в обеспечении лучшей спецификации интерфейса. –

+4

Кастинг, возможно, не бесплатный, но я не думаю, что это имеет какое-то отношение к дизайну класса. – assylias

3

Я предполагаю, что причина в том, что containsAllcontains, remove, removeAll) использует Object#equals для сравнения.

Возможно, вы можете использовать метод Object#equals в E, который может возвращать true для объектов некоторого неродственного класса. Не то, чтобы это было хорошей идеей, но это могла быть правильная реализация.

+1

Иногда это хорошая идея. Например, 'java.util.List' требует, чтобы' .equals() 'возвращал true для списков с тем же контентом, даже для разных классов списка. – newacct

1

Причина такая же, как для методов add и contains.

add принимает аргумент общего типа коллекции, поскольку в коллекцию могут быть добавлены только такие объекты. В этом весь смысл использования дженериков в коллекциях.

contains (а также другие методы в рамках коллекции, такие как remove и Map.get) принимает любой объект как параметр. Есть по крайней мере две причины.

Во-первых, как сказал Тобиас Брандт, могут быть объекты совершенно другого типа, которые являются «равными» (как определено их реализацией equals) для объекта в коллекции.

Во-вторых, каждая коллекция Collection<E> можно рассматривать как Collection<? extends D>, где D является супер класс Е, или даже в качестве Collection<?> (который является таким же, как Collection<? extends Object>). Если вы сделаете это, вы не сможете больше называть метод add, потому что его подпись будет выглядеть как add(?), а компилятор запрещает называть его, потому что он никогда не сможет обеспечить безопасность типа (и хорошо, что вы не можете позвонить add, потому что вы можете добавьте неправильные типы в коллекцию). Тем не менее, может быть полезно позвонить contains, и это всегда безопасно для типов, поэтому почему это не должно быть разрешено? Чтобы это разрешить, метод contains должен иметь Object как тип параметра, иначе его нельзя было бы назвать похожим на add.

Подписи addAll и containsAll просто следуют этому же принципу.

+1

«Тем не менее, может быть полезно использовать call contains, и это всегда безопасно для типов». Но это опять же потому, что 'Object.equals()' принимает все типы. Если 'Object.equals()' не принимает все типы, то это не всегда будет безопасным по типу. – newacct

Смежные вопросы