Вывод типа дженерики не удается? - PullRequest
8 голосов
/ 11 ноября 2010

Пример А

Изучите следующий фрагмент:

public class ExampleA {
   static class Pair<F,S> { }

   static <F,S> Pair<F,S> anyPair() { return null; }

   static <F,S> void process(Pair<F,S> p1, Pair<F,S> p2) { return; }

   public static void main(String[] args) {
      Pair<String,Integer> p = anyPair();

      process(p, anyPair()); // doesn't compile
   }
}

Может кто-нибудь объяснить, почему вывод типа работает для присвоения локальной переменной p, но не для второго фактического параметра process?


Пример B

Возможно, это проще понять:

public class ExampleB {     
   public static <E> void process(Set<E> s1, Set<E> s2) { return; }

   public static void main(String[] args) {
      process(new HashSet<String>(), Collections.emptySet()); // doesn't compile
   }
}

Тот же вопрос: почему он не компилируется?

Я надеюсь, что Collections.emptySet() просто подойдет для ANY параметризованного Set типа.

Ответы [ 2 ]

8 голосов
/ 11 ноября 2010

Ваш второй вызов anyPair() не имеет никакого способа определить его типы, поэтому по умолчанию он равен <Object, Object>.

. Компилятор разбивает process(p, anyPair()); на части и обрабатывает каждый по отдельности.Когда он делает это, он должен сначала обработать аргументы, чтобы определить их типы, которые затем могут быть использованы при обработке process.

Когда он переходит к обработке anyPair(), для этого нет информации о типе.кусок, потому что он не знает, что он является частью process на тот момент.По умолчанию он равен <Object, Object>, что приводит к несоответствию типов при просмотре process.

То же самое происходит со вторым примером.Collections.emptySet() должен быть обработан сам по себе, но у него нет способа определить необходимые типы.

Есть 2 способа решить эту проблему:

Первый - дать компилятору информацию, которую оннуждается в выводе типа так же, как вы делали это при первом вызове anyPair(), сохраняя его во временной переменной с правильным типом.

Вторым (благодаря @BalusC) является использование ExampleA.<String, Integer>anyPair(),Этот синтаксис явно устанавливает необходимые типы без необходимости смотреть за пределы вызова.

0 голосов
/ 10 декабря 2013

Почему:

Collections.emptySet() пытается определить тип возвращаемого значения. Это невозможно, потому что E может быть Object или String. Оба являются действительными совпадениями для процесса. Общие параметры всегда инвариантны по умолчанию, а не контравариантны. Это означает, что Integer extends Number, но List<Integer> делает , а не расширяет List<Number>. Однако расширяет List<? extends Number>.

Решения:

Используйте назначение для вывода типа:

public <E> void process(Set<E> s1, Set<E> s2) { return; }

public void main(String[] args) {
   Set<String> s = Collections.emptySet();  // add this line
   process(new HashSet<String>(), s);
}

Явно напишите тип:

public <E> void process(Set<E> s1, Set<E> s2) { return; }

public void main(String[] args) {
   process(new HashSet<String>(), Collections.<String>emptySet()); //notice <String>
}

Явно разрешить контравариантность (однако, возможно, это не то, что вам нужно):

public <E> void process(Set<E> s1, Set<? super E> s2) { // added "super"
  return;
}

public void main(String[] args) {
   process(new HashSet<String>(), Collections.emptySet());
}
...