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 и это единственное, что заботит компилятор.

Другие вопросы по тегам