Java泛型:不兼容的通配符捕获

时间:2014-06-09 17:21:25

标签: java generics bounded-wildcard

我有一个使用存储实体类型参数化的存储库接口。在其中的其他方法中,我有两个感兴趣:实例化实体的create()方法和保存实体的save()方法:

public interface INamedEntityRepository<T extends INamedEntity> {
    T create(String name);
    void save(T namedEntity);
}
...

接下来是关于如何使用此接口的片段。我收到一个编译错误,说明从create()返回的类型和传递到save()的类型不兼容。

INamedEntityRepository<? extends INamedEntity> repo = getEntityRepository();

INamedEntity toSave = repo.create("Named");
... // configure the entity more...
repo.save(toSave);
      ^

The method save(capture#7-of ? extends INamedEntity) in the type INamedEntityRepository<capture#7-of ? extends INamedEntity> is not applicable for the arguments (INamedEntity)。我能理解这一点,因为INamedEntity真的不一定是预期的类型。

但即便这样做

repo.save(repo.create("Named")));

无济于事:

The method save(capture#7-of ? extends INamedEntity) in the type INamedEntityRepository<capture#7-of ? extends INamedEntity> is not applicable for the arguments (capture#8-of ? extends INamedEntity)

有什么问题?如何正确处理这种情况? 提前谢谢。

4 个答案:

答案 0 :(得分:1)

您将无法在使用捕获通用定义(即?extends)声明的任何INamedEntityRepository实例上调用.save。这与您无法在列表上调用add的方式类似。

How can I add to List<? extends Number> data structures?

答案 1 :(得分:1)

问题是编译器不知道未知类型是两种用法的相同未知类型。解决方案是将类型“锁定”为两种用法的特定类型。例如,如果这是一种方法,您可以键入方法:

public <T extends INamedEntity> void someMethod(String name) {
    INamedEntityRepository<T> repo = getEntityRepository(); // may need cast here
    T toSave = repo.create(name);
    // do stuff with toSave
    repo.save(toSave);
}

您尚未显示getEntityRepository()的签名,因此可能需要强制转换,因此如果它也是类型化方法,则可以使用以下语法:

INamedEntityRepository<T> repo = MyClass.<T>getEntityRepository();

如果是静态,或

INamedEntityRepository<T> repo = this.<T>getEntityRepository();

如果是非静态的,则通过该类型传递。

答案 2 :(得分:0)

无论是否将repo.create("Named")的结果存储在变量中,编译器都会检查其类型,并且由于create返回的类型为INamedEntity,因此赢了&#39工作。要解决此问题,编译器必须知道create返回的内容是save可以接受的子类型,这正是您使用的时候通配符。

在这种特殊情况下,你当然知道类型是正确的,所以你可以使用这样的不安全的演员:

((INamedEntityRepository<INamedEntity>) repo).save(repo.create("Named"));

或者您可以将不安全操作的问题委托给其他位置,例如首先声明repo没有通配符。


如果getEntityRepository()实际上返回一些具体类型,而不是通配类型,则可以使用以下内容:

void <T extends INamedEntity> test() {
    INamedEntityRepository<T> repo = getEntityRepository();
    T toSave = repo.create("Named");
    repo.save(toSave);
}

现在看起来非常明显我输入了...

答案 3 :(得分:0)

Silly Freak和Bohemian的解锁方法确实给了我一个警告,让我在做getEntityRepository()时做一个演员。所以我在这里放了一个变种,它为我编译而没有任何警告:

protected abstract INamedEntityRepository<? extends INamedEntity> getEntityRepository();
...

// the method that extracts the code of interest
private <T extends INamedEntity> void test(String name, INamedEntityRepository<T> repo) {
   T toSave = repo.create(name);
   repo.save(toSave);
}
...

test("Named", getEntityRepository());