Интерфейсы, обобщения и ковариантные типы возвращаемых данных
Предположим, у меня есть интерфейс следующим образом:
public interface Foo<T> {
T doSomething();
}
Теперь разрешены оба следующих варианта?
public class Bar implements Foo<Number> { ... }
public class Bar2 extends Bar implements Foo<Integer> { ... }
С одной стороны, я, кажется, так не думаю, так как Bar2
"орудие Foo
дважды ", хотя Integer
это подкласс Number
, С другой стороны, разве это не случай ковариантного типа возврата на doSomething()
? Или компилятор не достаточно умен, чтобы обнаружить его как таковой?
2 ответа
Если Foo
должен быть интерфейсом:
Класс не может быть одновременно подтипом двух типов интерфейса, которые являются разными параметризациями одного и того же универсального интерфейса [...], или возникает ошибка времени компиляции.
Так что нет. ( JLS 8.1.5)
И в любом случае это не имело бы смысла, потому что вы бы удручали Bar
случается, чтобы вернуться из doSomething
, Может быть, это Double
,
С другой стороны, вы все равно можете сделать:
class Bar2 extends Bar {
@Override
public Integer doSomething() {...}
}
Или компилятор не достаточно умен, чтобы обнаружить его как таковой?
То, что мы и компилятор думаем, здесь не имеет значения. Компилятор привязан к спецификации языка, которая диктует ошибку.
Это не интерфейс, а класс (или абстрактный класс, в котором также отсутствует ключевое слово). abstract
), это должно быть как:
public interface Foo<T> {
T doSomething();
}
Кроме того
public class Bar implements Foo<Number> { ... }
public class Bar2 extends Bar implements Foo<Integer> { ... }
Даст вам ошибку времени компиляции, The interface Foo cannot be implemented more than once with different arguments: Foo<Number> and Foo<Integer>
Но если вы вместо этого сделаете:-
public class Bar implements Foo<Integer> { ... }
public class Bar2 extends Bar implements Foo<Integer> { ... }
Это не даст ошибку времени компиляции, и если вы реализуете doSomething()
в Bar2 это будет учитываться, когда вы делаете:
Bar2 bar2=new Bar2();
bar2.doSomething();
или он будет работать doSomething()
от Bar
и, очевидно, если вы делаете:-
Bar bar=new Bar();
bar.doSomething();
это будет учитывать doSomething()
из Bar
поскольку он имеет только один реализованный doSomething()
на этот раз, т.е. Bar
(который вы должны реализовать с Bar
реализует интерфейс Foo
:))