Java接口泛型转换问题

3

List 类型中的 add(capture#2-of ? extends IObject) 方法无法接受参数 (IDerived)

protected List<? extends IObject> getObjects()
{
    List<? extends IObject> objects = new ArrayList<IObject>();
    for (String id: item_ids)
    {
        IDerived object = (IDerived) readObject(id);
        objects.add(object); #error
    }
    return objects;
}

interface IDerived extends interface IVersionedObject extends interface IObject

如果我将对象的类型更改为List,则错误消失了,这没有任何意义,因为它必须对函数返回类型进行完全相同的转换。

2
你的 objects 应该被实例化为 new ArrayList<IDerived>() 吗? - biziclop
@biziclop,为什么不把它发布为答案呢? - finnw
我可能在回答中有些过火了。我以为你在问为什么而不是如何解决它。 - Andrew White
1
@finnw 因为我手头没有 Eclipse 进行编译测试,而且我也没有时间写解释。 - biziclop
3个回答

7

试试这个:

protected <T extends IObject> List<T> getObjects() {
    List<T> objects = new ArrayList<T>();
    for (String id: item_ids)
    {
        T object = (T) readObject(id);
        objects.add(object); // NO error
    }
    return objects;
}

编辑(简短说明):

这样,您的方法将能够与IObject的任何子类型很好地配合使用(列表和强制转换在内部变成了通用类型),并且返回类型将从调用者的预期中推断出来(正如您所期望的那样)。

针对评论,现在可以以下列方式调用getObjects()方法:

// This should only be used if readObject() behaves like
// getObjects() and is able to return any requested subtype.
// Otherwise, you'll get a ClassCastException when trying to get
// something from the derivedList in case readObject() put something
// else there (which is not a subtype of IDerived).
List<IDerived> derivedList = getObjects();

// This is the safe way to go in case you don't have
// full control over what readObject() returns.
// But if you're using it like this (all the time), you'd better
// return List<IObject> from getObjects() and get rid
// of generics.
List<? extends IObject> objectList1 = getObjects();
List<IObject> objectList2 = getObjects();

+1...而且如果调用者真的需要,他们总是可以将结果强制转换/存储为List<? extends IObject> - Mark Peters
哦,等等,我没有看到中间的未选中转换。那有点糟糕。 - Mark Peters

4
我不确定我是否能正确解释这个问题,但我会尝试。
List<? extends IObject> objects = new ArrayList<IObject>();

说列表是一个未知类型,它扩展了IObject。即使你在rhs上放置了new ArrayList<IObject>();,列表的泛型类型仍然没有被严格定义。换句话说,在分配引用时,泛型类型不会被“固定”。因此,当你调用objects.add(object);时,类型检查器不知道列表是否与IDerived兼容,因为列表可能是IVersionedObjectSomethingElse类型。尽管这在逻辑上是不可能的,但却是可以构造出来的。
List<? extends IObject> objects = null;
if (test) { 
    objects = new ArrayList<IObject>();
}
else {
    objects = new ArrayList<IVersionedObject2>();
}

objects.add( (IDerived) iDerivedObj ); // iDerived *might* not be compatible

-1

为什么要使用通配符?可以用 IDerived 替代 ? extends IObject

修改此行

List<? extends IObject> objects = new ArrayList<IObject>();

转换为:

List<IDerived> objects = new ArrayList<IDerived>();

并更改

protected List<? extends IObject> getObjects()

protected List<IDerived> getObjects()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接