接口中的嵌套参数化类型

Nested Parameterized types in interface

我有以下接口:

CacheKey接口:

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

缓存值接口:

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 中是不可能的吗?

您在这里错过了关键的一步:实施。

通常在实现中您要定义 ? extends Serializable 的类型。您不需要在 Cache 接口中实现它。

接口只需要知道它的泛型类型是什么,而不是它们的子代的泛型:这是为了实现。

看看下面的示例,了解我的确切意思。

补充:当您定义类似 Cache<CacheKey, CacheValue> 的内容时,您并不是在指代 class,而是在创建通用别名。 CacheKey 可以很容易地替换为 Blabla 并继续具有相同的行为。解决方案是使用 extends 来确保我们谈论的是类型。

这也是 Cache<CacheKey<...>> 没有编译的原因,因为 CacheKey 不是指 class 而是用作别名

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
    }
}

按照其他答案中的建议,使用 wildcard-bounded 泛型类型并不是一个好主意。

如果您这样声明 class:

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

那么您将永远无法有效地调用键或值上的 get(),因为它们只会 return SerializableSerializable 是一个 没用的 class (它与 Object 实际上在你的代码中几乎没有什么不同)。

相反,我会简单地声明 class,例如:

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);
}

但这真的很恶心,因为无论你在哪里直接使用 Cache 类型,你都必须携带所有这 4 个类型变量。

当然,您可以专门化接口来隐藏其中一些类型变量:

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

然后只需使用 StringIntegerCache。这样做的用处取决于您的应用程序。