3

为了减少项目中的代码重复,我在泛化一些类时出错,情况如下:

interface ClassRoot{}

public class ClassChild1 implements ClassRoot{
    // constructor is omitted for simplicity
    public Collection<ClassChild1> createList(){

        Collection<ClassChild1> col = getCollectionFromSomewhere();
        return col;
    }
}

public class Class1{
    protected <T extends ClassRoot> Collection<T> doSth(){
        Collection<T> myCol = null;
        ClassChild1 child = new ClassChild1();

        // here I get compile time assignment error
        // also (assuming that myCol not null) myCol.add(new ClassChild1());
        // is not permitted.
        myCol = child.createList();
        return myCol;
    }
}

这不是反对多态现象吗?我知道,例如,

List<Object> list1;

永远不能(也不应该为了类型安全而分配):

List<String> list2;

但是在这里我的情况不同,我指定了扩展某些特定类的类型参数,期望利用 OOP 多态性概念,并正确地执行正确的分配以实现正确的类。现在我有两个问题;

  • 有人可以清楚地解释为什么在 Java 中这是被禁止的,出于什么正当理由?
  • 如何通过使用类型参数或通配符来实现这样的功能?
4

5 回答 5

1

那作业

Collection<T> myCol = null;
myCol = child.createList();

将失败,因为您正在分配Collection<ClassChild1>Collection具有未知类型的参数化T,这可能是也可能不是ClassChild1

您已指定T为在调用站点推断的,因此想象以下代码:

Class1 c1;
Collection<ClassChild2> c = c1.doSth();

您可以看到,如果编译器允许您编译这样的doSth方法,则编译器会将自己绘制成一个角落,因为在该方法中您希望将一个ClassChild1实例添加到在本例中恰好包含ClassChild2.

于 2012-08-01T08:45:47.343 回答
0
Collection<ClassChild1> col = getCollectionFromSomewhere();
return col;

正在返回一个集合,但实际上您的 ClassChild1 不是一个集合。这肯定会产生编译时错误。确保方法的返回类型必须是集合类型或其后代

于 2012-08-01T08:54:15.413 回答
0

createList不提供Collection,这也是一个错误。

您不能将 的列表分配给ClassChild1的列表T

例如,如果您重命名RootClassAnimal,ClassChild1Cat. 如果现在的一个实例T不是 a Cat,那么您显然无法完成您想要的作业。

于 2012-08-01T09:03:15.117 回答
0

很难说是什么问题,因为:

public ClassChild1 createList(){

一定是错的(正如其他人所指出的那样)。它实际上是否说:

public Collection<ClassChild1> createList(){

这编译好:

import java.util.*;

interface ClassRoot {}

class ClassChild1 implements ClassRoot {

    public Collection<? extends ClassRoot> createList(){

        Collection<ClassChild1> col = new ArrayList<ClassChild1>();
        return col;
    }
}

class Class1{
    protected Collection<? extends ClassRoot> doSth(){
        Collection<? extends ClassRoot> myCol = null;
        ClassChild1 child = new ClassChild1();

        myCol = child.createList();
        return myCol;
    }
}

我已声明createList返回可分配给ClassRoot. 它返回的内容与此兼容。

然后我改变doSth了返回相同的东西,所以它也兼容。

缺点是你仍然不能调用:

myCol.add(child);

并且有一个很好的理由——即使我们不知道收藏品存在什么限制,但这并不意味着没有限制(关灯时房间不会消失)。

于 2012-08-01T09:04:17.590 回答
0

在此处考虑您的代码。您的 ClassChild 实现是,

public class ClassChild implements ClassRoot {
    public List<ClassChild> createList() {
        return Arrays.asList(new ClassChild[] {new ClassChild(), new ClassChild()});
    }


}

你的 Class1 是

public class Class1 {

    public static <T extends ClassRoot> Collection<T> doSth(){
        Collection<T> myCol = null;
        ClassChild child = new ClassChild();
        /*you should note here*/
        myCol =  (Collection<T>) child.createList();
        return  myCol;
    }
}

你必须停下来,在我提到的“你必须在这里注意”的地方停下来。如果你作为 Collection 进行强制转换,问题就解决了。但是谈到您的问题,您已经创建了一个名为 myCol 的集合,其中包含扩展 ClassRoot 的类型 T。但是 createList 将返回包含子类型 ClassRoot 的集合的保证是什么?所以你总是要明确地投射。

于 2012-08-01T09:36:08.480 回答