2015-03-21 3 views
7

Когда рекомендуется делать:Generic против шаблона неизвестные типы

public <E> boolean hasPropertyX(List<E extends User> alist); 

против

public boolean hasPropertyX(List<? extends User> alist); 

Казалось бы, они оба работают так же хорошо.

+1

'Это отражает тот факт, что параметр типа не используется для выражения какой-либо взаимозависимости между типом (ами) аргумент (ы), тип возвращаемого значения и/или тип throw.В отсутствие такой взаимозависимости общие методы считаются плохим стилем, и предпочтение отдается шаблонам. 'Спецификация языка Java 8 §4.5.1-2. В принципе, если есть зависимость от типа, выходящего за пределы метода, следует использовать первое. В противном случае используйте вторую. – Obicere

ответ

2

Явного именование общего типа, как E и не ? имеет следующие виды использования (насколько я могу думать):

0) Для того, чтобы связать тип возврата к некоторой части типа аргумента - например:

public <E> E getSomeElement(List<E> lst) { ... } 
//^If we don't name the argument type as having E, 
// then we can't specify the return type as being E 

1) для того, чтобы связать какую-то часть типа аргумента в какой-то части типа ограждающей:

class Storage<E> { 
    E item; 
    public void replace(Storage<E> st) { item = st.item; } 
    //^This wouldn't work if we wrote Storage<?> instead 
} 

2) для того, чтобы связать некоторые сочетание типов аргументов, типа возврата и типа окружения (см. # 0 и # 1).

Мы можем уйти с анонимным именем типа ?, если нам не нужен фактический тип. Вот простой пример:

boolean allEqual(List<?> lst, Object y) { 
    for (Object x : lst) { // Any reference can be stored as Object 
     if (!y.equals(x)) // equals takes an Object 
      return false; 
    } 
    return true; 
} 
//^We could also rewrite this example with List<E> and "E x". 

Другой пример:

int intSum(List<? extends Number> lst) { 
    int sum = 0; 
    for (Number x : lst) // We only care that the list element is a Number 
     sum += x.intValue(); 
    return sum; 
} 
//^We could also rewrite with List<E extends Number> and "E x". 

Альтернативное чтение: http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html

2

Я полагаю, в этом конкретном примере они оба работают эффективно так же, как с точки зрения проверки типов , Однако, если вы расширяете общий тип, чтобы требовать базу или суперкласс какого-либо класса, это может быть полезно. например

public <E extends User> boolean hasPropertyX(List<E> alist); 

Это, по крайней мере, навязывает, что вы получаете некоторый подкласс User.

EDIT

Вы можете использовать маску, чтобы достичь того же:

public boolean hasPropertyX(List<? extends User> alist); 

Но это не будет работать, если, например, вы хотите использовать общий для нескольких параметров:

public <E extends Automobile> void crashAutos(List<E> list1, List<E> list2); 

Это обеспечивает общий тип для обоих аргументов, тогда как следующий код не заставляет два списка содержать s AME Тип:

public void crashAutos(List<? extends Automobile> list1, List<? extends Automobile> list2); 

я мог бы назвать этот метод с двумя различными подклассами Automobile класса:

List<Car> cars = ... 
List<Truck> trucks = ... 
crashAutos(cars, trucks); 

Принимая во внимание, используя дженерики навязывает тот же тип для обоих аргументов.

+1

Вы можете сделать то же самое с шаблоном, хотя, по крайней мере, на примере 'drawAll' здесь: http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html – wrongusername

+0

Хорошая точка. Я склоняюсь к родовому значению по шаблону, поскольку Java склонна жаловаться на него меньше (непроверенные предупреждения о броске). – kuujo

4

Без типизированного возвращаемого значения единственное различие, о котором я могу думать, - это явный ввод первого способа объявления во время вызова метода.

Так, например, вы используете его внутри типизированного класса C<K extends String>

List<V extends String> input = ...; 
boolean var = obj.hasProperty<K>(input); 

поднимет ошибку компилятора. Но почему кто-то хочет это сделать ...

Хороший вопрос, даже если, скорее всего, ответ один и тот же.

2

Различия между воспроизведенных и подстановочные неизвестных типов:

  • , исполняющих отношения на типы аргументов метода (использование дженериков)
  • поддержки нескольких границ (использование Generics)
  • Supporting обе верхние и нижние границы (Использование Wildcard)

родственный вопрос:

When to use generic methods and when to use wild-card?

1

Используйте ? extends, когда вам нужно только получить из списка:

User getElement(List<? extends User> list, int i) { 
    return list.get(i); 
} 

Использование ? super, когда вам нужно только добавить к списку:

void addElement(List<? super User> list, User u) { 
    list.add(u); 
} 

Использование E extends когда вы и необходимо восстановить и добавить:

<E extends User> void swapElements(List<E> list, int i, int j) { 
    E temp = list.get(i); 
    list.set(i, list.get(j)); 
    list.set(j, temp); 
} 
  • ? extends User: Мы не знаем точный тип списка, но мы можем извлечь из него User.
  • ? super User: Мы не знаем точный тип списка, но мы можем поместить в него User.
  • E extends User: Мы не обязательно знать точный тип списка, но оно соответствует ограничениям, что:
    • Мы даем его фактического введите имя E.
    • Мы знаем, что E составляет не менее User.
    • Мы можем как вернуть E из списка, так и положить E в Список.

Смотрите также:

+0

Это должен быть принятый ответ. Это единственный вопрос, который действительно правильно отвечает на вопрос. Остальные все читали как «Я не знаю, но это может случиться, или это может быть не кто действительно знает?». – Adowrath

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