У меня есть следующий интерфейс:
Слайсер
public interface Slicer {
Optional<Map<String, ? extends Serializable>> pointer();
}
из которых у меня есть реализация:
DynamoDbSlicer
public abstract class DynamoDbSlicer implements Slicer {
@Override
public abstract Optional<Map<String, AttributeValue> pointer();
}
где AttributeValue
взят из AWS SDK и определяется как:
public final class AttributeValue implements SdkPojo, Serializable, ToCopyableBuilder<AttributeValue.Builder, AttributeValue> {
Обратите внимание, что он реализует Serializable
.
Я получаю ошибку компилятора в абстрактном методе в DynamoDbSlicer
:
pointer() in DynamoDbSlicer clashes with pointer() in Slicer; attempting to use incompatible return type
Что мне не хватает?
На первый взгляд это выглядит так, как будто оно должно компилироваться, потому что Java (начиная с версии 1.5) имеет ковариантность возвращаемого типа. Это означает, что переопределяющий метод может объявить возвращаемый тип, являющийся подтипом возвращаемого типа исходного метода.
Похоже, что Optional<Map<String, Serializable>>
является подтипом Optional<Map<String, ? extends Serializable>>
, но это не так. Интересно, что если вы удалите часть Optional
из обоих возвращаемых типов, это скомпилируется.
interface Slicer {
Map<String, ? extends Serializable> pointer();
}
abstract class DynamoDbSlicer implements Slicer {
@Override
public abstract Map<String, Serializable> pointer();
}
Он компилируется, потому что Map<String, Serializable>
является подтипом Map<String, ? extends Serializable>
— экземпляр первого можно присвоить переменной второго типа.
Однако добавление Optional
приводит к сбою компиляции по той же причине, что и List<Dog>
не List<Animal>
-- дженерики неизменны. В этой аналогии Dog
подобен конкретному подтипу, который соответствует Map<String, Serializable>
, а Animal
подобен общему типу, который соответствует Map<String, ? extends Serializable>
. Точно так же, как List<Dog>
не является List<Animal>
, Optional<Map<String, Serializable>>
не является Optional<Map<String, ? extends Serializable>>
.
Самый простой способ скомпилировать это, не удаляя бит Optional
, — это точно соответствовать типу.
abstract class DynamoDbSlicer implements Slicer {
@Override
public abstract Map<String, ? extends Serializable> pointer();
}
Если это не соответствует вашим требованиям, вам нужно будет создать параметр типа в интерфейсе, который ваш класс может предоставить в качестве аргумента типа.
interface Slicer<T extends Serializable> {
Optional<Map<String, T>> pointer();
}
abstract class DynamoDbSlicer implements Slicer<AttributeValue> {
@Override
public abstract Optional<Map<String, AttributeValue>> pointer();
}
Параметр типа позволяет это компилировать.
Спасибо за объяснение! Я думаю, что смогу заставить код работать без необязательных
В разделе «интересно» под «удалить необязательную часть» я имел в виду возвращаемый тип Map<String, ? extends Serializable>
в интерфейсе и Map<String, AttributeValue>
в классе, которые будут компилироваться.
Согласно PECS Можете ли вы попробовать использовать
super
?