在方法参数中使用通配符

时间:2014-10-08 13:31:07

标签: java generics collections wildcard

我已经声明了一个方法,其参数是Number的子类型,如下所示:

public static void organizeData(java.util.List<? extends Number> c) {
    //operation on the list
}

我可以将任何非参数化列表作为参数传递。那么使用通配符<? extends Number>是什么意思?

List newList = new LinkedList<>();
ClassName.organizeData(newList);

为什么我从Object获取c类型的元素?而不是Number类型?有没有办法只允许一种类型的子类型作为参数传递,而不是允许非参数化的参数呢?

3 个答案:

答案 0 :(得分:6)

你在这里问三个问题,我将分别回答。顺便说一下,你会发现阅读Java Generics FAQ - considered by many to be the canonical reference on the subject

很有价值
  1. 那么使用通配符<? extends Number>是什么意思?
  2. 当参数化参数是Number的子类,比如Integer时,您需要使用通配符。例如:

    import java.util.LinkedList;
    import java.util.List;
    
    public class NumberTest {
        public static void main(String... args) {
            List<Integer> newList = new LinkedList<Integer>();
            organizeData(newList); // This works!
            anotherMethod(newList); // Doesn't compile
        }
    
        private static void organizeData(List<? extends Number> list) {
    
        }
    
        private static void anotherMethod(List<Number> list) {
    
        }
    }
    

    第二个方法调用因编译错误而失败:

    NumberTest.java:9: error: method anotherMethod in class NumberTest cannot be applied to given types;
                            anotherMethod(newList); // Doesn't compile
                            ^
      required: List<Number>
      found: List<Integer>
      reason: actual argument List<Integer> cannot be converted to List<Number> by method invocation conversion
    1 error
    

    1. 为什么我从Object获取c类型的元素?而不是类型Number
    2. 你在第二种情况下获得Object类型的原因是因为you are using the Raw Type.如果你因为这个原因可以避免使用原始类型你就永远不会使用它 - 你失去了编译器类型的所有优点检查。


      1. 是否有办法只允许某种类型的子类型作为参数传递,而不允许非参数化参数?
      2. 您无法以您描述的方式阻止Heap Pollution,因为有人总是可以转换为Raw类型,然后转换为所需的任何内容。泛型是仅编译时构造,are ultimately erased after compilation.这就是为什么未经检查的类型转换警告只能通过注释来抑制。

答案 1 :(得分:1)

您必须记住,Java泛型是一个编译时构造,以帮助进行类型安全。在运行时,类型擦除会将所有泛型类型转换为Object,并在需要时添加转换。如果通过使用原始类型(编译器为其提供警告)绕过编译时检查,则绕过了泛型的好处(编译时类型检查和运行时安全转换)。

答案 2 :(得分:0)

Java中的泛型是通过Type erasure实现的。即:您在&lt;&gt;中指定的类型信息在运行时期间不存在。在编译期间,java编译器使用&lt;&gt;中给出的信息。确保一切都井然有序。但是,如果您未在&lt;&gt;中指定类型正如您对变量'newList'所做的那样,没有什么可以阻止您将它传递给'organizData'方法。