我对Java中的通配符有一些错误的概念

时间:2016-03-26 19:08:06

标签: java generics wildcard

我试图了解如何在Java中使用通配符。

import java.util.List;
public class WildcardError{
    void foo(List<?> i){
        i.set(0, i.get(0));
    }
}

public class WildcardFixed{
    void foo(List<?> i){
        fooHelper(i);
    }
    private <T> void fooHelper(List<T> l){
        l.set(0, l.get(0));
    }
}
  • 为什么第一类不能编译而第二类可以编译?
  • 编译器看到通配符时会发生什么?把它变成对象?与第一堂课中的void foo(Object i)类似,或void foo(List<Object> i)
  • 类型推断如何在第二类中起作用?

3 个答案:

答案 0 :(得分:2)

在第一个示例中,您可能已经传递了List<String>,因此如果您使用Object(结果为l.get(0))设置元素,则实际上您无法安全地执行此类型操作。 一种近视观点。

在第二个示例中,l.get(0)是T,完全适合l.set

答案 1 :(得分:2)

解释此https://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html

的好教程
  

...

     

然而,向它添加任意对象是不安全的:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
  

由于我们不知道c的元素类型代表什么,我们无法向其添加对象。 add()方法接受类型为E的参数,即集合的元素类型。当实际类型参数为?时,它代表某种未知类型。我们传递给add的任何参数都必须是这种未知类型的子类型。由于我们不知道它是什么类型,我们无法传递任何内容。唯一的例外是null,它是每种类型的成员。

     

另一方面,给定List,我们可以调用get()并使用结果。结果类型是未知类型,但我们始终知道它是一个对象。因此,可以安全地将get()的结果赋给Object类型的变量,或者将其作为期望Object类型的参数传递。

答案 2 :(得分:1)

当我这样做时:

import java.util.ArrayList;
import java.util.List;

public class Wildcard {

    public static void main(String[] args) {
    WildcardFixed wf = new WildcardFixed();
    List<Integer> li = new ArrayList<>();
    li.add(0);
    wf.foo(li);
    System.out.println("Success");
    }
}

class WildcardError{
    void foo(List<?> i){
        i.set(0, i.get(0));
    }
}

class WildcardFixed{
    void foo(List<?> i){
        fooHelper(i);
    }
    private <T> void fooHelper(List<T> l){
        l.set(0, l.get(0));
    }
}

WildcardError类无法使用消息

进行编译
  

类型List中的方法set(int,capture#1-of?)不适用于参数(int,capture#2-of?)

在简洁的英语中,编译器说它不知道i中包含什么类型的东西,即它不知道get()返回的是什么类型的东西,它不知道set()采用什么类型的参数,因此无法保证set()操作会成功。

然而,

WildcardFixed编译只是因为我们向编译器保证,无论l中的任何类型如何,get的结果都是同一类型,T ,作为set的参数类型。编译器不需要太多工作,但它需要的不仅仅是?

但是,你可以变得更简单。如果您将类型参数T放入原始foo()方法中,则所有内容都会编译并完美运行。

import java.util.ArrayList;
import java.util.List;

public class Wildcard {

    public static void main(String[] args) {
    WildcardFixed wf = new WildcardFixed();
    List<Integer> li = new ArrayList<>();
    li.add(0);
    wf.foo(li);
    System.out.println("Success WildcardFixed");

    WildcardWithT wt = new WildcardWithT();
    wt.foo(li);
    System.out.println("Success WildcardWithT");
    }
}

class WildcardError{
    void foo(List<?> i){
        i.set(0, i.get(0));
    }
}

class WildcardFixed{
    void foo(List<?> i){
        fooHelper(i);
    }
    private <T> void fooHelper(List<T> l){
        l.set(0, l.get(0));
    }
}

class WildcardWithT {
    <T> void foo(List<T> i) {
    i.set(0, i.get(0));
    }
}