Тип вывод странность

1

Итак, у меня есть класс 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);
Теги:
generics
casting
types
type-inference

1 ответ

1
Лучший ответ

Это старая родовая инвариантность, поднимающая голову снова.

Для каждого вызова 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.

  • 0
    спасибо, это не так чисто, как хотелось бы, но немного лучше. (хотя я использовал статический метод для получения вывода типа, я могу также использовать конструктор в этом случае, если это лучшее, что я могу получить). Почему я не могу привести TypeX.class к Class<?> ?
  • 0
    См. Мое редактирование: К сожалению, у меня нет хорошего ответа на вопрос, почему именно так, но я дал свое понимание этого.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню