Использование обобщенного обобщенного типа с подстановочными знаками, как предлагается в других ответах, не очень хорошая идея.
Если вы объявите свой класс следующим образом:
public interface Cache<K extends CacheKey<?>, V extends CacheValue<?>> {
}
, тогда вы быникогда не сможет с пользой вызвать get()
для ключа или значения, потому что они только когда-либо будут возвращать Serializable
.И Serializable
- это бесполезный класс (он практически не отличается от 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
.Полезность этого зависит от вашего приложения.