2011-02-07 3 views
0

У меня есть необщий код вроде этого:Общий типа для аргумента, который также является возвращаемым значение

class List { 
    List intersect(List out, List other) { 
    if (out == null) out = new List(); 
    // insert elements common to $this and $other into $out 
    return out; 
    } 
} 

Этого метода безопасно использовать смешанные типы содержащихся объектов:

  1. other может содержат объекты более конкретного типа (подкласс)
  2. out может содержать объекты менее определенного типа (суперкласс)

. (А не от реального кода)

List<Number> my; 
List<Integer> other; 
List<Object> result = new(); 

result = my.intersect(result, other); 

Что мне нужно, поэтому:

class List<T> { 
    <R super T> List<R> intersect(List<R> out, List<? extends T> other); 
} 

Однако это не компилируется. В FAQ Generics указано, что нижняя граница для параметра типа не имеет смысла, но я не вижу этого приведенного выше примера.

Ближайший я получаю:

List<? super T> intersect(List<? super T> out, List<? extends T> other); 

Но это требует явного приведения возвращаемого значения на месте вызова.

Можно ли создать этот метод таким образом, чтобы он был безопасным для всех существующих обычаев?

UPDATE: В случае это не очевидно: (., Который является интерфейсом кстати) это не java.util.List но пользовательский класс. Исключительные части были опущены, но подпись метода точно такая же, как показано (за исключением уровня доступа).

UPDATE2: По типу безопасности я имею в виду как можно более ограниченные возможности. В идеале все параметры должны соблюдать оба правила, изложенные выше.

+1

Является 'intersect' статический метод? Если нет, я немного смущен, почему есть сам экземпляр списка и 2 аргумента списка? –

+0

Просто примечание: знаете ли вы о методе List.retainAll: http://download.oracle.com/javase/6/docs/api/java/util/List.html#retainAll%28java.util.Collection% 29 ? – Puce

+0

Это не статично, и это не java.util.List –

ответ

0

Вы пытаетесь нарушить правила ковариации и контравариантности. Предположим, вы ожидаете List<Object> в качестве вашего out параметра, а T - некоторый класс A, и есть еще один класс B, который расширяет A. Разработчик вашего intersect метода создает List<A> для out, что кажется прекрасным, поскольку A расширяет B. Но вызывающий получает список, который он предполагает, что он является List<Object>. Теперь вызывающий пытается вставить кучу C (не связанную с A и B, за исключением того, что все три происходят из объекта в какой-то момент), и все это взрывается.

Другими словами, это невозможно.

+0

Хотя я чувствую, что действительно невозможно ограничить типы столько, сколько захочу, я не совсем следую вашему примеру. В любом случае, если 'out'T' менее специфичен, чем' T', а затем вставить все, что соответствует (подклассу) 'out.T', в список. –

0

Редактировать: Так как я полностью неправильно понял вопрос, вот новая попытка ответить.

Я сначала хотел ответить

class List<T> { 
     /** 
     * Creates the intersection of this list with a second list, 
     * adding the resulting elements to another list. 
     * @param out the List to which the output should be added. 
     *  If null, a new List will be created. 
     * @param second the second list to intersect with this list. 
     * @return out, with the intersection of left an right added 
     */ 
    <R super T, S extends R> 
     List<R> intersect(List<R> out, List<S> second); 
} 

но, как вы выяснили, он не компилируется. Да, я думаю, что это было бы правильное использование ключевого слова super. Похоже, что невозможно сделать что-то подобное этой компиляции.

(Я знаю, что вы сказали, что S должен быть подтипом T, а не только S, но я по-прежнему не вижу этого по мере необходимости. На самом деле S может быть чем угодно, но оставляя его подтипом R дает больше свободы для реализации это действительно не имеет значения здесь)

Интересно, можно сформулировать как статический метод:..

class List<T> { 
     /** 
     * Creates the intersection of two Lists, 
     * adding the resulting elements to another list. 
     * @param out the List to which the output should be added. 
     *  If null, a new List will be created. 
     * @param left the first list to intersect. 
     * @param right the second list to intersect 
     * @return out, with the intersection of left an right added 
     */ 
    static <R, T extends R, S extends R> 
     List<R> intersect(List<R> out, List<T> left, List<S> right) { 
     // TODO 
     return out; 
    } 
} 

Или, если вам не нужен T и S внутри метода:

class List<T> { 
     /** 
     * Creates the intersection of two Lists, 
     * adding the resulting elements to another list. 
     * @param out the List to which the output should be added. 
     *  If null, a new List will be created. 
     * @param left the first list to intersect. 
     * @param right the second list to intersect 
     * @return out, with the intersection of left an right added 
     */ 
    static <R> 
     List<R> intersect(List<R> out, List<? extends R> left, List<? extends R> right) { 
     // TODO 
     return out; 
    } 
} 

Конечно, этот статический способ сказать это не работает, если вы хотите переопределить этот метод в подклассах.

+0

В ваших примерах R никоим образом не ограничивается 'List.T'. Другими словами, это терпит неудачу. Условие 2: 'out' может содержать объекты менее определенного типа (суперкласс) –

+0

ОК, похоже, я неправильно понял ваш вопрос ... Я адаптирую свой ответ позже. –

+0

Итак, новый ответ. Кстати, если в ваших образцах кода был комментарий javadoc, я бы сразу понял это :-) –

0
<R> List<R> intersect(List<R> out, List<? extends R> other) 
{ 
    for(R x : other) 
     if(this.contains(x)) 
      out.add(x); 
    return out; 
} 

boolean contains(Object x){ ... } 

обратите внимание, что contains() принимает Object, вместо T

+0

Как уже упоминалось в другом ответе, ваш пример позволяет использовать неограниченный тип возвращаемого значения, что более разрешительно, чем мое условие 2 (T extends out. Т). –

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