Это пример из стороннего библиотечного API, но упрощенный.
Скомпилировано с Oracle JDK 8u72
Рассмотрим эти два метода:
<X extends CharSequence> X getCharSequence() {
return (X) "hello";
}
<X extends String> X getString() {
return (X) "hello";
}
Оба сообщают о предупреждении «непроверенный актерский состав» - я понимаю, почему. Меня сбивает с толку то, почему я могу позвонить
Integer x = getCharSequence();
а это компилируется? Компилятор должен знать, что Integer
не реализует CharSequence
. Призыв к
Integer y = getString();
выдает ошибку (как и ожидалось)
incompatible types: inference variable X has incompatible upper bounds java.lang.Integer,java.lang.String
Может кто-нибудь объяснить, почему это поведение считается действительным? Как это будет полезно?
Клиент не знает, что этот вызов небезопасен - код клиента компилируется без предупреждения. Почему компиляция не предупредит об этом / не выдаст ошибку?
Кроме того, чем он отличается от этого примера:
<X extends CharSequence> void doCharSequence(List<X> l) {
}
List<CharSequence> chsL = new ArrayList<>();
doCharSequence(chsL); // compiles
List<Integer> intL = new ArrayList<>();
doCharSequence(intL); // error
Попытка пройти List<Integer>
дает ошибку, как и ожидалось:
method doCharSequence in class generic.GenericTest cannot be applied to given types; required: java.util.List<X> found: java.util.List<java.lang.Integer> reason: inference variable X has incompatible bounds equality constraints: java.lang.Integer upper bounds: java.lang.CharSequence
Если это сообщается как ошибка, почему Integer x = getCharSequence();
нет?
Integer x = getCharSequence();
будет компилироваться, но кастинг на RHSInteger x = (Integer) getCharSequence();
не скомпилируетсяОтветы:
CharSequence
являетсяinterface
. Поэтому, даже еслиSomeClass
не реализует,CharSequence
было бы вполне возможно создать классПоэтому вы можете написать
потому что предполагаемый тип
X
является типом пересеченияSomeClass & CharSequence
.Это немного странно в случае,
Integer
потому чтоInteger
является окончательным, ноfinal
не играет никакой роли в этих правилах. Например, вы можете написатьС другой стороны,
String
не являетсяinterface
, так что было бы невозможно расширить,SomeClass
чтобы получить подтипString
, потому что Java не поддерживает множественное наследование для классов.В этом
List
примере вы должны помнить, что генерики не являются ни ковариантными, ни контравариантными. Это означает, что еслиX
является подтипомY
, неList<X>
является ни подтипом, ни супертипомList<Y>
. ПосколькуInteger
не реализуетCharSequence
, вы не можете использоватьList<Integer>
в своемdoCharSequence
методе.Вы можете, однако, получить это для компиляции
Если у вас есть метод , который возвращает
List<T>
как это:ты можешь сделать
Опять же, это потому, что предполагаемый тип есть
Integer & CharSequence
и это подтипInteger
.Типы пересечений возникают неявно, когда вы указываете несколько границ (например
<T extends SomeClass & CharSequence>
).Для получения дополнительной информации, здесь часть JLS, где объясняется, как работают границы типов. Вы можете включить несколько интерфейсов, например
но только первая граница может быть неинтерфейсом.
источник
&
в общее определение. +1<T extends String & List & Comparator>
Это нормально, но<T extends String & Integer>
это не так, потому чтоInteger
это не интерфейс.Collections.emptyList()
а такжеOptional.empty()
. Они возвращают реализации универсального интерфейса, но ничего не хранят.final
во время компиляции, будетfinal
во время выполнения.getCharSequence()
обещает вернуть все,X
что нужно вызывающему, включая возврат расширяющегоInteger
и реализующего типа,CharSequence
если это необходимо, и в соответствии с этим обещанием правильно разрешить присваивать результатInteger
. Это метод,getCharSequence()
который не работает, поскольку он не выполняет свое обещание, но это не ошибка компилятора.Тип, который выводится вашим компилятором до назначения,
X
-Integer & CharSequence
. Этот тип кажется странным, потому что онInteger
является окончательным, но это совершенно допустимый тип в Java. Затем его бросаютInteger
, что совершенно нормально.Существует ровно один из возможных значений для
Integer & CharSequence
типа:null
. Со следующей реализацией:Следующее задание будет работать:
Из-за этого возможного значения нет причин, по которым назначение должно быть неправильным, даже если оно явно бесполезно. Предупреждение было бы полезно.
Настоящая проблема - это API, а не сайт вызова
На самом деле, я недавно написал в блоге об этом шаблоне анти-API дизайн . Вы (почти) никогда не должны разрабатывать универсальный метод для возврата произвольных типов, потому что вы (почти) никогда не можете гарантировать, что выведенный тип будет доставлен. Исключением являются такие методы, как
Collections.emptyList()
, в случае которых пустота списка (и стирание универсального типа) является причиной, по которой любой вывод<T>
будет работать:источник