【问题标题】:Nested Parameterized types in interface接口中的嵌套参数化类型
【发布时间】:2019-06-01 03:20:09
【问题描述】:

我有以下接口:

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

【问题讨论】:

    标签: java generics parameterized


    【解决方案1】:

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

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

    接口只需要知道它的泛型类型是什么,而不需要知道其子类的泛型:这是用于实现的。

    看看下面的例子,了解我的意思。

    添加:当您定义类似 Cache&lt;CacheKey, CacheValue&gt; 的内容时,您并不是指的是类,而是创建了一个通用别名。 CacheKey 可以很容易地被 Blabla 替换并继续具有相同的行为。解决方案是使用extends 来确保我们谈论的是类型。

    这也是Cache&lt;CacheKey&lt;...&gt;&gt;没有编译的原因,因为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
        }
    }
    

    【讨论】:

    • K extends CacheKeyV extends CacheValue 将由于使用原始类型而产生编译器警告,这抵消了使用泛型的全部好处。
    • @VGR 您确定这些警告吗?我不确定是否会失去使用泛型的好处,因为实现会强制它们..
    • @VGR 它没有发出任何警告,但我还是修改了代码,为了清楚起见,并且通过显式放置泛型类型,您可以强制实现不适用于原始类型。但没有发出警告。
    • 尝试在启用所有警告的情况下进行编译。在命令行上,使用-Xlint 完成。当我这样做时,我得到Cache.java:12: warning: [rawtypes] found raw type: CacheKey
    • 感谢 YassinHajaj - 您的后续更新让我找到了正确的解决方案。
    【解决方案2】:

    按照其他答案的建议,使用通配符限制的泛型类型不是一个好主意。

    如果你这样声明你的类:

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

    那么您将永远无法对键或值有效地调用get(),因为它们只会返回Serializable。而Serializable 是一个无用 类(实际上它与您的代码中的Object 几乎没有区别)。

    相反,我会简单地声明类:

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

    然后声明put 方法采用CacheKey&lt;K&gt;CacheValue&lt;V&gt;

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

    因为最终,CacheKey&lt;K&gt;CacheValue&lt;V&gt; 的所有实现都应该是无法区分的。

    如果您真的想强制CacheKey&lt;K&gt;CacheValue&lt;V&gt; 为特定类型,则需要添加更多类型变量:

    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。这样做的用处取决于您的应用程序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-10
      • 1970-01-01
      • 2015-12-07
      • 2013-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多