Переопределение нескольких обобщенных функций c с помощью одной функции с общими верхними границами - PullRequest
1 голос
/ 14 апреля 2020

У меня есть два разных интерфейса

interface ColumnSet {
   <V, I extends Column & Input<V>> V getValue(I column);
}

interface ParameterSet {
   <V, I extends Parameter & Input<V>> V getValue(I value);
}

, где типы Column и Parameter являются просто маркерными интерфейсами для остановки использования Column s, где должно быть Parameter s, и наоборот , За кулисами мне хотелось бы иметь один класс, который реализует их оба следующим образом:

class ObjectSet implements ColumnSet, ParameterSet {
   @Override public <V, I extends Input<V>> V getValue(I input) {
       ...
   }
}

Логически кажется, что ObjectSet.getValue должно быть допустимым переопределением для ColumnSet.getValue и ParameterSet.getValue, так как он принимает любой Input<V> в качестве аргумента, который ограничен сверху Column & Input<V> и Parameter & Input<V>. Однако Java 9 не распознает это как переопределение того, что кто-либо из них сообщает The method getValue() of type ObjectSet must override or implement a generic supertype method.

Является ли это ограничением обобщений в Java или я что-то упускаю из фундаментального?

( Очевидно, я не могу создать два отдельных метода в ObjectSet из-за того, что они имеют одинаковое стирание, что оставляет мне альтернативу давать разные имена для двух getValue методов в интерфейсах, которых я пытаюсь избежать).

1 Ответ

2 голосов
/ 15 апреля 2020

Согласно (§8.4.8.1) , метод экземпляра m1 переопределяет другой метод экземпляра m2, если подпись m1 является подписью (§8.4 .2) подписи m2.

Другими словами, подпись переопределенного метода должна быть такой же, как подпись переопределенного метода или стирание (§4.6) подписи переопределенного метода .

Таким образом, параметры переопределенного метода не могут быть заменены параметрами менее определенного типа c в методе переопределения. Вы можете прочитать, почему это так здесь .


В вашем случае подпись ObjectSet#getValue отличается от подписи ColumnSet#getValue и ее подписи удаления (Object getValue(Column column)). То же самое относится к ParameterSet#getValue.


Как было указано @ samabcde , если базовые методы объявлены следующим образом:

<V, I extends Column & Input<V>> V getValue(I column);
<V, I extends Parameter & Input<V>> V getValue(I value);

Вы можете реализовать их следующим образом:

@Override
public <V, I extends Column & Input<V>> V getValue(I value) {
    return doGetValue(value);
}

@Override
public <V, I extends Parameter & Input<V>> V getValue(I value) {
    return doGetValue(value);
}

private <V, I extends Input<V>> V doGetValue(I value) { ... }

И если базовые методы объявлены следующим образом:

<V, I extends Input<V> & Column> V getValue(I value);
<V, I extends Input<V> & Parameter> V getValue(I value);

Вы можете реализовать их удаление только следующим образом:

@Override
public Object getValue(Input value) { ... }

Поскольку ни один из этих параметров не выглядит хорошо, я рекомендую изменить код следующим образом:

public interface Input<R, V> {
    // ...
}
public interface ObjectSet<R> {
    <V> V getValue(Input<R, V> input);
}
public class ObjectSetImpl<R> implements ObjectSet<R> {
    @Override
    public <V> V getValue(Input<R, V> input) {
        // ...
    }
}

Теперь вы можете легко создавать ObjectSet экземпляры, которые принимают только Input с указанным c параметром типа R:

public interface Column<V> extends Input<Column<?>, V> {}
ObjectSet<Column<?>> columnSet = new ObjectSetImpl<>();

Если вам не нравится писать ObjectSet<Column<?>>, вы можете создать реализацию с более подходящим именем ObjectSet<Column<?>>, которая делегирует всю работу до ObjectSetImpl:

public class ColumnSet extends DelegatingObjectSet<Column<?>> {}
ColumnSet columnSet = new ColumnSet();

Где DelegatingObjectSet:

abstract class DelegatingObjectSet<R> implements ObjectSet<R> {
    // you can use dependency injection here
    private final ObjectSet<R> delegate = new ObjectSetImpl<>();

    @Override
    public <V> V getValue(Input<R, V> input) {
        return delegate.getValue(input);
    }
}
...