Тип вывода с использованием типа возврата, wild card и типа пересечения

Я пытаюсь объявить интерфейс, содержащий метод, который вернет список вещей, которые реализуют как Comparator<Object>, так и Action, i.e.

<T extends Comparator<Object> & Action> List<T> getThings();

Это компилируется отлично, но проблема возникает, когда я пытаюсь вызвать этот метод. Я хочу иметь возможность сделать это:

List<Action> things = getThings();
List<Comparator<Object>> things = getThings();

Когда я пытаюсь сделать это, я получаю следующую ошибку компиляции:

 incompatible types; no instance(s) of type variable(s) T exist so that
 java.util.List<T> conforms to java.util.List<javax.swing.Action>
 found   : <T>java.util.List<T>
 required: java.util.List<javax.swing.Action>

Не работает и следующее:

List<? extends Action> things = getThings();
List<? extends Comparator<Object>> things = getThings();

Другим способом достижения этого эффекта является создание пустого интерфейса, который расширяет как Comparator<Object>, так и Action и использует это как возвращаемый тип, т.е.

public interface ComparatorAction extends Comparator<Object>, Action { }
List<ComparatorAction> getThings();

Но я не хочу этого делать. Должен быть способ сделать то, что я хочу, правильно? Любые идеи?

Спасибо!

P.S. У меня тяжелое время подходит для хорошего заголовка для этого сообщения, поэтому не стесняйтесь его менять.

Ответ 1

Вы также можете параметризовать метод (ы), из которого вы вызываете getThings(). Например:

public static <U extends Comparator<Object> & Action> void main(String[] args) {
    List<U> l = getThings();
    Action a = l.get(0);
    Comparator<Object> c = l.get(0);
}

Ответ 2

List<? extends Action> things = getThings();
List<? extends Comparator<Object>> things = getThings();

edit Это была моя первая реакция. Тогда я подумал об этом, и я не думал, что вывод может работать, поэтому я удалил ответ.

Проверка спецификации снова, они должны работать. Он компилируется в JDK7, но сбой в JDK6. Я думаю, там ошибка, которую они исправили.

изменить 2 нет... Я снова прочитал спецификацию (JLS3 # 15.12.2.8), и теперь я не думаю, что вывод должен работать. JDK6 был прав в отказе от вывода. (Я сомневаюсь, что JDK7 представил новую ошибку, возможно, что правила вывода обновляются, поэтому JDK7 корректен в соответствии с новыми правилами. Я не уверен)

В соответствии с JLS3, сначала есть подстановочный знак, вводится новый параметр типа W, который имеет верхнюю границу Action. Тогда вывод имеет следующие начальные ограничения:

List<W> >> List<T>
Comparable >> T
Action  >> T

Первое ограничение ограничивает ограничение на равенство T=W и что он делает вывод.

Теперь компилятор проверяет, удовлетворяет ли предполагаемая T ее границам, то есть,

W :< Comparable
W :< Action

Ответ отрицательный, 1-я оценка не может быть выполнена. (IntelliJ показывает хорошее сообщение об ошибке (лучше, чем javac's): "Inferred type": extends Action (т.е. W) для параметра типа "T" не находится в пределах его границы, должен реализовывать Comparable ")

edit 3 вопрос заключается в том, должен ли быть захват подстановочных знаков до вывода. это мне непонятно. Если не должно быть, то имеем

List<? extends Action> >> List<T>
Comparable >> T
Action  >> T

что дает

T :< Action
T :< Comparable

поэтому T=Comparable & Action

Ответ 3

Когда вы возвращаете такой List<T>, T относится к некоторому (неизвестному) типу, который является подтипом Action и Comparator<Object> и является (предпочтительно самым низким) общим супертипом все элементы в списке. Если такого типа не существует, вы, вероятно, столкнетесь с проблемами.

Если вам все равно, что T, вы можете использовать переменную типа, как предлагает Дейв Коста, или вы можете использовать подстановочный знак

List<? extends Action> l = getThings();
List<? extends Comparator<Object>> l2 = getThings();