2014-12-26 4 views
5

Почему Java может вывести общий предок из нескольких типов с верхним ограничением, но не из менее ограниченных типов?Вывод типа Java с нижними ограниченными типами

Более конкретно, рассмотрим следующие примеры:

static class Test { 

    static <T> T pick(T one, T two) { 
     return two; 
    } 

    static void testUpperBound() { 
     List<? extends Integer> extendsInteger = new ArrayList<>(); 

     // List<? extends Integer> is treated as a subclass of List<? extends Number> 
     List<? extends Number> extendsNumber = extendsInteger; 

     // List<? extends Number> is inferred as the common superclass 
     extendsNumber = pick(extendsInteger, extendsNumber); 
    } 

    static void testLowerBound() { 
     List<? super Number> superNumber = new ArrayList<>(); 

     // List<? super Number> is treated as a subclass of List<? super Integer> 
     List<? super Integer> superInteger = superNumber; 

     // The inferred common type should be List<? super Integer>, 
     // but instead we get a compile error: 
     superInteger = pick(superNumber, superInteger); 

     // It only compiles with an explicit type argument: 
     superInteger = Test.<List<? super Integer>>pick(superNumber, superInteger); 
    } 
} 
+0

Ваш тест компилируется для меня без аргумента явного типа с использованием 1.8.0_25. – Alex

+0

@Alex Я использую 1.7. Возможно, это так исправлено. – shmosel

ответ

1

Я думаю, что я могу объяснить, почему Java различает нижне- ограниченного и ограниченного типа.

Попытка вывести общую нижнюю границу может завершиться неудачно, если используются несовместимые границы, например Integer и Long. Когда мы используем верхнюю границу, всегда можно найти общую верхнюю границу, в данном случае List<? extends Number>. Но нет общей нижней границы List<? super Integer> и List<? super Long>. Единственным безопасным вариантом в случае такого конфликта было бы возвращение List<? extends Object>, синонимом List<?>, что означает «a List неизвестного типа».

Теперь, возможно, мы могли бы прибегнуть к этому только тогда, когда на самом деле существуют противоречивые рамки, в отличие от случая в моем вопросе. Но, возможно, было решено сделать простой выход и не предполагать, что существует общая нижняя граница, если явно не указано.

0

Я использую 1.8.0_25, и я получаю ошибку компиляции. Ошибка, однако, заключается не в том, что вызов выбрать плохо, а в переменную, в которую вы хотите внести результат. Повторяя свой пример:

static void testLowerBound() { 
    List<? super Number> superNumber = new ArrayList<>(); 
    List<? super Integer> superInteger = superNumber; 

    // this gets the error 
    superInteger = pick(superNumber, superInteger); 
    // this doesn't 
    pick(superNumber, superInteger); 

    // what's happening behind is 
    List<? extends Object> behind = pick(superNumber, superInteger); 
    superInteger = behind; 
    // that last line gets the same compilation error 
} 

Если посмотреть на то, как Т замещен в вызове, параметры используются в качестве списка, потери информации о нижних границах.

О умозаключении: каждый? не совсем «то, что может быть назначено ...», но «конкретный тип, который я не хочу назвать, который может быть назначен ...». Это важно, потому что в вашем примере вы получаете 3 переменные, 1 для каждого списка, а другой, другой, для результата выбора. Теперь, благодаря декларации выбора, подстановка для T должна удовлетворять иерархии классов параметров. В первом случае вам нужна замена для < # 1 extends Integer> и < # 2 extends Number>. # 2 может быть двойным, так что лучший ключ у вас есть, что # 3 расширяет число. Во втором случае вам нужно заменить < # 1 super Integer> и < # 2 super Number>. Теперь это означает, что # 2 может быть любым из Number, Object, Serializable; # 1 добавляет к этому списку Comparable и Integer. Комбинациями могут быть Number, Object (и T должен быть Object); или Serializable, Integer (и T может быть Serializable), поэтому лучший ключ, который он имеет, состоит в том, что T является списком неизвестного типа, расширяющего Object.

Конечно, это может достигнуть только номер, но вы не можете получить две оценки для одной и той же переменной типа, так же должен впускать его в то

+0

Я не вижу ваших рассуждений. Так же, как мы переносим привязку к ближайшему обычному суперклассу ('Number') с' extends', мы должны перенести его на ближайший общий подкласс ('Integer') с' super'. – shmosel

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

  • Нет связанных вопросов^_^