На первый взгляд, похоже, что он должен компилироваться, потому что 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
делает невозможным компиляцию по той же причине, по которой a 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();
}
Параметр type позволяет это компилировать.