PECS не работает на типах возвращаемых данных с интерфейсом
Рассмотрим следующий пример:
class ClsA {}
class ClsB {}
interface IntA {}
interface IntB {}
И у меня есть 2 очень похожие методы:
static <T extends ClsA> T returnC() { // Here T extends the class
return null;
}
static <T extends IntA> T returnI() { // Here T extends the interface
return null;
}
И тогда метод вызывает:
ClsA ac = returnC(); // This works fine based on inference.
IntA ai = returnI(); // Similarly this works fine based on inference.
Но рассмотрим ниже 2:
ClsB bc = returnC(); // ERROR as expected.
Ошибка затмения:
Несоответствие границ: универсальный метод returnC() типа Testing не применим для аргументов (). Предполагаемый тип ClsB&ClsA не является допустимой заменой ограниченного параметра
<T extends ClsA>
Но следующий код компилируется нормально:
IntB bi = returnI(); // Works fine
Почему это для интерфейса, родовые границы не учитываются в возвращаемых типах?
1 ответ
Волшебные слова здесь - сырье и множественное наследование.
Давайте сначала посмотрим на ваш returnC
метод:
static <T extends ClsA> T returnC() {
return null;
}
Тип T
ограничен ClsA
Это означает, что если вы вызываете сырой returnC
метод, тогда тип возвращаемого значения будет просто ClsA
,
Правда, когда у вас есть это утверждение: ClsA ac = returnC();
компилятор успешно завершает компиляцию, потому что необработанный тип возвращаемого значения метода ClsA
, который совместим с типом ac
,
Необработанный тип возврата также является причиной, по которой оператор ClsB bc = returnC();
не компилируется.
Теперь давайте посмотрим на returnI
метод:
static <T extends IntA> T returnI() { // Here T extends the interface
return null;
}
Здесь параметр типа связан с IntA
только.
Это, однако, не означает, что тип замены для T
должен реализовывать только IntA
- тип может реализовать IntA
а также IntB
в то же время. Заявления как IntB bi = returnI();
разрешены, потому что тип может реализовывать несколько интерфейсов, но не может реализовывать несколько классов.
Рассмотрим этот класс:
class SomethingReallyCool implements IntA, IntB { }
Этот тип является допустимой заменой для параметра типа returnI()
и доказательство тому - это утверждение:
IntB bi = YourClass.<SomethingReallyCool>returnI();
Зачем? Потому что это класс, который реализует IntA
и это единственное, что заботит компилятор.