Вложенные параметризованные типы в интерфейсе - PullRequest
2 голосов
/ 01 июня 2019

У меня есть следующие интерфейсы:

Интерфейс CacheKey:

public interface CacheKey<K extends Serializable> extends Serializable {
    K get();
}

Интерфейс CacheValue:

public interface CacheValue<V extends Serializable> extends Serializable {
    V get();
}

Интерфейс кэша:

public interface Cache<CacheKey<K extends Serializable>, CacheValue<V extends Serializable>> {
}

Интерфейс кэша не компилируется.Я получаю сообщение об ошибке: [13,35]> ожидается

, т. Е. После того, как компилятору CacheKey не понравится другая угловая скобка открытия:

public interface Cache<CacheKey<
                               ^

Разве это невозможно в Java?

Ответы [ 2 ]

1 голос
/ 01 июня 2019

Здесь вы упускаете важный шаг: реализацию.

Обычно в реализации вы хотите определить тип ? extends Serializable.Вам не нужно реализовывать это в интерфейсе Cache.

Интерфейсу нужно только знать, какими будут его обобщенные типы, а не обобщенные элементы их дочерних элементов: это для реализации.

Посмотрите на приведенный ниже пример, чтобы понять, что именно я имею в виду под этим.

Добавление: Когда вы определяете что-то вроде Cache<CacheKey, CacheValue>, вы не имеете в виду классы, но вы создаетеуниверсальный псевдоним.CacheKey можно легко заменить на Blabla и продолжать вести себя так же.Решение состоит в том, чтобы использовать extends, чтобы убедиться, что мы говорим о типе.

По этой же причине Cache<CacheKey<...>> не скомпилировался, поскольку CacheKey не относится к классу, аиспользуется как псевдоним

public interface CacheKey<K extends Serializable> extends Serializable {
    K get();
}

public interface CacheValue<V extends Serializable> extends Serializable {
    V get();
}

public interface Cache<K extends CacheKey<? extends Serializable>, V extends CacheValue<? extends Serializable>> {
    void put(K key, V value);
}

public class CacheImpl implements Cache<CacheKey<String>, CacheValue<String>> {
    @Override
    public void put(CacheKey<String> key, CacheValue<String> value) {
        // do whatever
    }
}
0 голосов
/ 03 июня 2019

Использование обобщенного обобщенного типа с подстановочными знаками, как предлагается в других ответах, не очень хорошая идея.

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

public interface Cache<K extends CacheKey<?>, V extends CacheValue<?>> {
}

, тогда вы быникогда не сможет с пользой вызвать get() для ключа или значения, потому что они только когда-либо будут возвращать SerializableSerializable - это бесполезный класс (он практически не отличается от Object на самом деле внутри вашего кода).

Вместо этого я просто объявил бы класс следующим образом:

public interface Cache<K extends Serializable, V extends Serializable> {

и затем объявляем, что метод put принимает CacheKey<K> и CacheValue<V>:

  void put(CacheKey<K> key, CacheValue<V> value);

, потому что, в конечном счете, все реализации CacheKey<K> и CacheValue<V> должны быть неразличимы.

Если вы действительно хотите, чтобы CacheKey<K> и CacheValue<V> имели определенные типы, вам нужно добавить еще переменные типа:

public interface Cache<
    K extends Serializable, CK extends CacheKey<K>,
    V extends Serializable, CV extends CacheValue<V>> {
  void put(CK key, CV value);
}

, ноэто действительно довольно грубо, так как вам придется носить с собой все эти 4 переменные типа, где бы вы ни использовали тип Cache.

Конечно, вы можете специализировать интерфейс, чтобы скрыть некоторые из этих переменных типа:

interface StringKey extends CacheKey<String> {}
interface IntegerValue extends CacheValue<Integer> {}
interface StringIntegerCache extends Cache<String, StringKey, Integer, IntegerValue> {}

, а затем просто используйте StringIntegerCache.Полезность этого зависит от вашего приложения.

...