Java Generics Wildcard混淆

时间:2015-01-02 03:07:11

标签: java generics

我最近在学习Java Generics,并且只是尝试了#Java; Java Generics FAQ"。

关于通配符参数化类型的问题(#304)有点困惑我,非常感谢你的帮助。

代码示例:

class Box<T> { 
  private T t; 
  public Box(T t) { this.t = t; } 
  public void put(T t) { this.t = t;} 
  public T take() { return t; } 
  public boolean equalTo(Box<T> other) { return this.t.equals(other.t); } 
  public Box<T> copy() { return new Box<T>(t); } 
}

class Test { 
  public static void main(String[] args) { 
    Box<?> box = new Box<String>("abc");
    box.put("xyz");     // error 
    box.put(null);     // ok

    String s = box.take();  // error 
    Object o = box.take();  // ok

    boolean equal = box.equalTo(box);  // error {confused}
    equal = box.equalTo(new Box<String>("abc")); // error {confused}

    Box<?> box1 = box.copy();   // ok 
    Box<String> box2 = box.copy();  // error 
  } 
}

无法弄清楚为什么下面两个被调用的方法会失败:

boolean equal = box.equalTo(box);
equal = box.equalTo(new Box<String>("abc"));

由于

4 个答案:

答案 0 :(得分:7)

Box<?> box = new Box<String>("abc");
box.put("xyz");     // error 
String s = box.take();  // error 
  1. 在OOP中,您正在使用多态。因此,在编译时,框对象的类型为Box<?>

  2. Type<?>这称为未知通配符。因为您不知道键入的Box类型,您只能从该对象读取,并且您只能使用读取为Object实例的对象。这就是收到box.put("xyz")错误并收到String s = box.take()错误的原因。

  3. 其次:

    boolean equal = box.equalTo(box);
    

    收到equalTo Box<T>而不是Box<?>。正如我上面解释的那样,T代表任何一个类,但?只意味着未知的通配符,这两个术语不一样。

    还有其他一点,你应该知道。在Java中,泛型是在编译时。与其他如C#相比,泛型是在运行时。

    以下是有关通配符的参考链接:Java wildcard

    希望这有帮助:)

答案 1 :(得分:5)

equalTo方法需要Box<T>,而不是Box<?>。当您拥有Box<?>类型的对象时,您无法将Box<String>甚至Box<?>作为参数添加到equalTo,因为这两个框的T类型可能不一样。

请记住,编译器将在编译时使用对象的静态类型。

所以,这将失败:

Box<?> b = new Box<String>();
b.equalTo(new Box<String>("abc");

但这不会:

Box<String> b = new Box<String>();
b.equalTo(new Box<String>("abc");

答案 2 :(得分:4)

  • Box<?>是“一盒未知”。
  • Box<? extends String>将是“一盒至少是弦乐的东西”
  • Box<String> 肯定是“一盒字符串”。
  • 因此,如果我们有一个“未知的盒子”,我们会尝试在其中放置一个类型(比如一个字符串)。编译器不确定。它是一盒未知的。如果它实际上只接受整数怎么办?
  • 我们可以将null放入“未知框”吗?肯定。
  • 我们从“未知盒子”中得到什么?至少是一个对象。

鉴于此,

Box<?> box = new Box<String>("abc");

要求编译器忘记<String>并假设box是“一盒未知”。您看到的错误指出编译器不能再对参数进行类型检查。它无法检查给定类型是否属于未知类型(null除外)。

答案 3 :(得分:3)

  

无法弄清楚为什么下面两个被调用的方法会失败

他们失败了,因为这是通配符的工作方式。

通配符代表“我们不知道的某种类型”。

让我们做一个更简单的课程。

class Holder<T> {
    private T obj;

    void set(T obj) {
        this.obj = obj;
    }

    T get() {
        return obj;
    }
}

如果我们有Holder<?>,我们就如何访问它有特殊规则。特别是,我们不能使用泛型参数调用方法。这是因为我们不知道它是什么类型:这样做会不安全。

当我们有Holder<?>时,我们会有(概念上):

class Holder<?> {
    private X obj;

    void set(X obj) {
        this.obj = obj;
    }

    Object get() {
        return obj;
    }
}

X表示该类型对我们是禁止的,因为我们不知道它是什么。

  • get会返回Object,因为这是唯一可以确定obj
  • 的课程
  • set根本无法调用

我认为这可能看起来很奇怪,因为如果你的equalTo最初被宣布为

public boolean equalTo(Box<?> other);
然后你就可以打电话了。但是,只需将T替换为?即可使用通配符。