接口中的嵌套参数化类型

时间:2019-06-01 03:20:10

标签: java generics parameterized

我有以下界面:

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

3 个答案:

答案 0 :(得分:0)

在未编译的代码段中,CacheKey实际上是一个类型参数(不引用上面创建的接口),这就是为什么在该位置不允许再有尖括号。

如果您想限制Cache接口的通用类型,则可以使用以下内容:

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

    default Serializable dummyMethod(K key, V value) {
        return key.get();
    }
}

我们可以在此处使用通配符(?,因为CacheKeyCacheValue的通用类型已经绑定到Serializable

答案 1 :(得分:0)

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

通常要在实现中定义? 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
    }
}

答案 2 :(得分:0)

使用其他答案中建议的通配符绑定的通用类型并不是一个好主意。

如果您这样声明课程:

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

但这确实很麻烦,因为无论您在哪里直接使用Cache类型,都必须携带这4个类型变量。

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

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

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