Итак, у меня есть класс Tuple2
следующим образом:
public final class Tuple2<T1, T2> {
private final T1 mItem1;
private final T2 mItem2;
public T1 getItem1() { return mItem1; }
public T2 getItem2() { return mItem2; }
public Tuple2(final T1 pItem1, final T2 pItem2) {
mItem1 = pItem1;
mItem2 = pItem2;
}
public static <TItem1, TItem2> Tuple2<TItem1, TItem2>
Create(final TItem1 pItem1, final TItem2 pItem2) {
return new Tuple2<>(pItem1, pItem2);
}
}
и я пытаюсь создать экземпляр List
Tuple2
s, но вывод типа кажется странным. Я ожидал, что смогу сделать что-то вроде этого:
// doesn't work
// note that Type2 extends Type1, and Type3 extends Type2
final List<Tuple2<String, Class<?>>> list = Arrays.asList(
Tuple2.Create("1", Type1.class),
Tuple2.Create("2", Type2.class),
Tuple2.Create("3", Type3.class)
);
// still doesn't work
final List<Tuple2<String, Class<? extends Type1>>> list = Arrays.asList(
Tuple2.Create("1", Type1.class),
Tuple2.Create("2", Type2.class),
Tuple2.Create("3", Type3.class)
);
Ни один из них не работает, если я передаю Class<?>
Или Class<? extends Type1>
Class<? extends Type1>
.
Вместо этого мне нужно:
final List<Tuple2<String, ? extends Class<? extends Type1>>> list = Arrays.asList(
Tuple2.Create("1", Type1.class),
Tuple2.Create("2", Type2.class),
Tuple2.Create("3", Type3.class)
);
Но если я хочу определить ссылку на один из этих Tuple2
я должен написать:
final Tuple2<String, ? extends Class<? extends Type1>> item = list.get(0);
Это одно уродливое имя типа... Есть ли способ упростить это? Почему он должен быть Tuple2
"того, что расширяет Class
чего-то, что расширяет Type1
", а не просто " Class
чего-то"?
Единственный более простой способ, который я нашел, - использовать Class
raw type, который, кажется, обескуражен и нуждается в кастинге:
final List<Tuple2<String, Class>> list = Arrays.asList(
Tuple2.Create("1", (Class)Type1.class),
Tuple2.Create("2", (Class)Type2.class),
Tuple2.Create("3", (Class)Type3.class)
);
final Tuple2<String, Class> item = list.get(0);
Это старая родовая инвариантность, поднимающая голову снова.
Для каждого вызова Create
, типы выводятся как:
<String, Class<Type1>>
<String, Class<Type2>>
<String, Class<Type3>>
Как вы, возможно, знаете, List<Dog>
не является List<Animal>
и здесь применяется то же понятие. Tuple2<String, Class<Type1>>
не является Tuple2<String, Class<?>>
но это Tuple2<String,? extends Class<?>>
Tuple2<String,? extends Class<?>>
.
Итак, для трех Tuple2
типов Tuple2
:
Tuple2<String, Class<Type1>>
Tuple2<String, Class<Type2>>
Tuple2<String, Class<Type3>>
Тип, который у них есть, - Tuple2<String,? extends Class<? extends Type1>>
Tuple2<String,? extends Class<? extends Type1>>
Tuple2<String,? extends Class<? extends Type1>>
.
Насколько я знаю, единственный способ обойти это - предоставить свидетеля типа для каждого вызова:
final List<Tuple2<String, Class<?>>> list = Arrays.asList(
Tuple2.<String, Class<?>>Create("1", Type1.class),
Tuple2.<String, Class<?>>Create("2", Type2.class),
Tuple2.<String, Class<?>>Create("3", Type3.class)
);
Это должно сработать.
Кажется, что это должно работать:
final List<Tuple2<String, Class<?>>> list = Arrays.asList(
Tuple2.Create("1", (Class<?>)Type1.class),
Tuple2.Create("2", (Class<?>)Type2.class),
Tuple2.Create("3", (Class<?>)Type3.class)
);
Но он не компилируется. Компилятор выполняет преобразование и отображает Class<?>
Как Class<capture of?>
. Таким образом, выведенные типы, с которыми вы закончите, - это что-то вроде:
<String, Class<capture #1 of ?>>
<String, Class<capture #2 of ?>>
<String, Class<capture #3 of ?>>
И еще три разных типа друг от друга.
Я буду честен: я понятия не имею, почему это работает именно так, как есть. Я не смог найти окончательного ответа на это. Спектр говорит об этом:
- Если T i является аргументом типа подстановочного символа [...] формы?, То S i является переменной нового типа [...].
Что, по-видимому, подразумевает capture of?
рассматривается как новый тип и отличается от него ?
,
Мне кажется, что вывод просто имеет пробел по отношению к шаблону с версии 7.
TypeX.class
кClass<?>
?