【问题标题】:Using Wildcards in Java在 Java 中使用通配符
【发布时间】:2014-10-23 17:38:22
【问题描述】:
import java.util.ArrayList;
import java.util.List;

public class WildCardNumber {

    public static void main(String[] args) {
        List<EvenNumber> le = new ArrayList<>();
        List<? extends NaturalNumber> ln = le;
        ln.add(new NaturalNumber(50));//  *   Compile time error
        ln.add(new EvenNumber(46));   // **   Compile time error 
    }

}

class NaturalNumber {

    private int n;

    public NaturalNumber(int n) {
        this.n = n;
    }

}

class EvenNumber extends NaturalNumber {

    public EvenNumber(int n) {
        super(n);
    }

}

在 Oracle 文档中研究通配符时,我发现了上述代码。

根据来源,变量“ln”不能接受任何“NaturalNumber”,因为它是“EvenNumber”内容的列表。我尝试向它添加一个“EvenNumber”对象。这也不被接受。

变量“ln”似乎是文档中提到的只读对象。你们能解释一下为什么这个对象是只读的吗?(我可以添加空值)如果我们不能添加“NaturalNumber”,为什么我们也不能添加“EvenNumber”?既然根据通配符我们已经指定,变量“ln”可以接受“NaturalNumber”的子类型,而“EvenNumber”是子类型?

【问题讨论】:

    标签: java generics wildcard


    【解决方案1】:

    这是一个经典问题。它被禁止的原因是因为如果它被允许:

        List<EvenNumber> le = new ArrayList<>();
        List<? extends NaturalNumber> ln = le;
        ln.add(new NaturalNumber(50));
        ln.add(new EvenNumber(46));  
        EvenNumber even = le.get(0); // ClassCastException
    

    我们通过 le 声明保证所有数字都必须是偶数。但是如果你被允许在那里添加一个 NaturalNumber,那么这个保证就会失效。

    【讨论】:

      【解决方案2】:

      类型安全检查基于所涉及对象的声明类型。本声明

      List<? extends NaturalNumber> ln = le;
      

      描述了一个(对一个)对象ln 的引用,其类型参数在编译时不精确地知道,实际上在程序执行期间的不同点可能会有所不同。为该变量分配对其类型更精确已知的对象的引用并不重要,因为 (1) 这不会更改 ln 的声明类型,以及 (2) 因为可以重新分配不同参数化的列表之后。 (在给定的代码中实际上并没有发生的事情是无关紧要的。)

      事实上,你不能将任何元素添加到列表ln,因为你(明确地)不知道它接受什么类型的元素。但是,您可以从中检索元素:

      le.add(new EvenNumber(0));
      NaturalNumber n = ln.get(0);
      

      如果你指定一个下限,你就会遇到相反的情况:

      List<? super EvenNumber> lq = le;
      NaturalNumber n = lq.get(0);      /* type safety error */
      

      情况正好相反:您无法确定列表 lq 中有哪些类型的对象,因为它可能是对 List&lt;Object&gt; 的引用(尽管您总是可以这样做:

      Object o = lq.get(0);
      

      .) 但是,您可以确定无论实际的类型参数是什么,它都与 添加 EvenNumbers:

      一致
      lq.add(new EvenNumber(12));
      

      【讨论】:

        【解决方案3】:

        您不能添加到 ln,因为这可能会导致运行时错误。 C# 和 Scala 更好地适应所谓的协变/逆变,其中泛型参数始终用作输出/输入参数。 C#/Scala 会让你安全地向下/向上转换。集合和列表在输入和输出容量中都使用泛型参数,因此您不能安全地允许强制转换,因此即使在 C#/Scala 中这些类型也是不变的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-09-04
          • 2014-01-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-06-06
          • 1970-01-01
          相关资源
          最近更新 更多