简体   繁体   English

Java泛型与多态

[英]Java Generics & Polymorphism

I had an error while generalizing some classes in order to reduce code duplicates in my project, the case is as follows: 我在推广某些类时出错,以减少项目中的代码重复,案例如下:

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

Isn't that against the polymorphism phenomenon? 那不是针对多态现象吗? I know that for example, 我知道,例如,

List<Object> list1;

can never be (and should not be assigned for typesafety) to: 永远不会(并且不应该被指定为类型安全):

List<String> list2;

But here my case is different, i specified the Type Parameter extending some specific class expecting to make use of OOP Polymorphism concept, and doing assignments right exactly implementing right class. 但在这里我的情况不同,我指定了类型参数,扩展了一些特定的类,期望利用OOP多态性概念,并且正确地执行正确的类来完成赋值。 Now I have 2 questions; 现在我有2个问题;

  • May someone clearly explain why is that is forbidden in Java, for what justifiable reason? 有人可以清楚地解释为什么在Java中禁止这样做,原因是什么?
  • How can I implement such a functionality, by using type params or wildcards? 如何通过使用类型参数或通配符来实现这样的功能?

The assignment 分配

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

will fail because you are assigning Collection<ClassChild1> to a Collection parameterized with an unknown type T , which may or may not be ClassChild1 . 将失败,因为您正在将Collection<ClassChild1>分配给使用未知类型T参数化的Collection ,该类可能是也可能不是ClassChild1

You have specified T as inferred at the call site, so imagine the following code: 您已在呼叫站点处指定T作为推断,因此请设想以下代码:

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

You can see that the compiler would paint itself into a corner if it allowed you to compile such a doSth method since inside that method you want to add a ClassChild1 instance to a collection that in this example happens to hold instances of ClassChild2 . 您可以看到,如果编译器允许您编译这样的doSth方法,编译器会将自己绘制到一个角落,因为在该方法中您希望将ClassChild1实例添加到集合中,在此示例中恰好包含ClassChild2实例。

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

is returning a collection but actually your ClassChild1 is not a collection. 正在返回一个集合,但实际上你的ClassChild1不是一个集合。 This will definitely create compile time error. 这肯定会产生编译时错误。 Make sure that the return type of the method must be a collection type or its descendant 确保方法的返回类型必须是集合类型或其后代

createList does not provide a Collection, this is also an error. createList不提供Collection,这也是一个错误。

You cannot assign a list of ClassChild1 to a list of T . 您不能将ClassChild1列表分配给T列表。

For example, if you rename RootClass to Animal , ClassChild1 to Cat . 例如,如果您重命名RootClassAnimalClassChild1Cat If now one instance of T would not be a Cat , you clearly cannot do the assignment you want. 如果现在T一个实例不是Cat ,你显然无法完成你想要的任务。

It's hard to tell what the problem is, because: 很难说出问题所在,因为:

public ClassChild1 createList(){

Must be wrong (as others have noted). 一定是错的(正如其他人所说)。 Does it actually say: 它真的说:

public Collection<ClassChild1> createList(){

This compiles okay: 编译好了:

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

I've declared createList to return something that is assignable to ClassRoot . 我已经声明createList返回的东西是分配给ClassRoot What it returns is compatible with that. 它返回的内容与此兼容。

Then I've changed doSth to return the same thing, so it is also compatible. 然后我改变了doSth以返回相同的东西,所以它也是兼容的。

The downside is that you still cannot call: 缺点是你还是不能打电话:

myCol.add(child);

And for a good reason - even though we don't know what restriction exists on the collection items, that doesn't mean that there is no restriction (the room doesn't disappear when you turn the light off). 并且有充分的理由 - 即使我们不知道收集物品存在什么限制,这并不意味着没有限制(当你关灯时房间不会消失)。

factored your code here . 在这里考虑你的代码。 Your ClassChild implementation is, 您的ClassChild实现是,

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


}

and your Class1 is 而你的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;
    }
}

You have to stop and at the place where I have mentioned as "You have to note here". 你必须停下来,并在我提到的地方“你必须在这里注意”。 If you do a cast as Collection , the problem is solved. 如果您将演员表作为集合进行,则问题就解决了。 But coming to your question, you have created a collection named myCol which contains type T that extends ClassRoot. 但是回答你的问题,你已经创建了一个名为myCol的集合,其中包含扩展ClassRoot的类型T. But what is the guarantee that createList will return a collection that contains subtypes ClassRoot ? 但是什么是createList将返回包含子类型ClassRoot的集合的保证? So you always to have cast explicitly. 所以你总是要明确地演员。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM