【问题标题】:Java polymorphism and listsJava 多态性和列表
【发布时间】:2014-01-14 16:31:20
【问题描述】:

假设我有以下类结构

public abstract class MyClass{}

public class MyClassA extends MyClass{}

public class MyClassB extends MyClass{}

如何创建包含 MyClassA 和 MyClassB 元素且无异议的 MyClass 元素列表?

例如

    List<MyClass> myList = new LinkedList<MyClassA>();

【问题讨论】:

  • List&lt;MyClass&gt; list = ...// pick your favorite.
  • 不是这样,我得到一个错误“Required List but found List
  • 列表 extends MyClass> 也可以。
  • A List&lt;MyClassA&gt; 不能与 MyClassBMyClass 对象一起使用,因此您的操作是错误的。如果您向我们展示,我们可以进一步解释。

标签: java list polymorphism extends


【解决方案1】:

根据您的需要,您有多种选择。您可以使用普通的 List&lt;MyClass&gt; 类型的对象。这允许您添加MyClassAMyClassB 类型的对象。不能将 List 类型对象设置为类型为 List&lt;MyClass&gt; 的变量。这是由于一个叫做variance的概念。大多数支持泛型(参数化多元主义)的编程语言中存在三种类型的差异。

不变性

不变性不允许一个人在任一方向上偏离一个类型。默认情况下,Java 中的泛型就是这种情况,这就是为什么您不能将List&lt;MyClassA&gt; 分配给List&lt;MyClass&gt; 的变量。 想象以下场景:

List<MyClassA> listA = new List<MyClassA>();
List<MyClass> list = (List<MyClass>) listA; // Produces a compilation error...
list.add(new MyClassB()); // ... because this would be a big no-no

协方差

协方差允许您使用指定的参数类型或其任何子类型。在 Java 中,您使用 extends 关键字来指定协方差:List&lt;? extends MyClass&gt;。允许将 List 类型的对象分配给这样的变量。但是,使用协方差,您不能在没有显式转换的情况下将任何对象添加到通用对象实例中。那就是你不能做以下事情:

List<? extends MyClass> list = new LinkedList<MyClassA>();
list.add(new MyClassA());

这只是合乎逻辑的。毕竟,您无法确切知道您正在处理的 List 类型。也可以是List&lt;MyClassB&gt;

逆变

逆变是协方差的反面,使用 super 关键字指定:List&lt;? super MyClassA&gt;。允许将List&lt;MyClass&gt;(但不是List&lt;MyClassB&gt;)类型的对象分配给这样的变量。通过逆变,您可以添加指定类型参数的任何子类型(类型层次结构中的下限)。也就是说,它允许执行以下操作:

List<? super MyClass> list = new LinkedList<MyClass>();
list.add(new ChildA());

但是,您无法确切知道对象是什么类型的 List。也就是说,您只能将存储在列表中的对象直接分配给 Object 类型的变量,而无需显式强制转换:

Object o = list.get(0);
if (o instanceof MyClassA) {
  MyClassA mca = (MyClassA) o;
  //...
}

我希望这有助于澄清一些事情。

【讨论】:

    【解决方案2】:

    多态性是对象具有多种形式的能力。 OOP 中最常见的多态性使用发生在使用父类引用来引用子类对象时。父类引用可以保存其任何子类的对象。因此,父类的集合可以保存子类对象。例如:

     List<MyClass> myClassList = new ArrayList<MyClass>();
    

    【讨论】:

      【解决方案3】:

      您可以使用像List&lt;? extends MyClass&gt; myList = new ArrayList&lt;MyClassA&gt;(); 这样的通配符,但这只会让您从列表中获取项目。添加项目并不安全,因为您不知道List 的底层类型是什么。您必须直接将您的项目添加到列表的实际实例中,例如:

      List<? extends MyClass> myList = null;
      
      ArrayList<MyClassA> actualListA = new ArrayList<MyClassA>();
      myList = actualListA;
      MyClassA myClassA = new MyClassA();
      actualListA.add(myClassA);
      myList.add(new MyClassA()); //Nope, you can't do this because myList could be assigned to an array of MyClassB items like the following.
      
      //Reassign
      ArrayList<MyClassB> actualListB = new ArrayList<MyClassB>();
      myList = actualListB;
      MyClassB myClassB = new MyClassB();
      actualListB.add(myClassB);
      
      //Get item
      MyClass item = myList.get(0);// This will give you the myClassB object.
      

      【讨论】:

        【解决方案4】:

        使用List 接口来实现您的具体/抽象类。像这样。

        List<MyClass> list = new ArrayList<MyClass>();
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-06-14
          • 1970-01-01
          相关资源
          最近更新 更多