Java 泛型通配符混淆

Java Generics Wildcard confusion

我最近在学习 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"));

谢谢

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");
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() 收到错误。

其次:

boolean equal = box.equalTo(box);

equalTo 收到 Box<T> 而不是 Box<?>。正如我上面所解释的,T 代表任何 class,但 ? 仅表示未知的通配符,这两个术语并不相同。

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

这里有一个关于通配符的参考link:Java wildcard

希望对您有所帮助:)

Can not figure out why below two method called will fail

他们失败了,因为这就是通配符的工作原理。

通配符代表"some type that we don't know about anymore".

让我们做一个简单的class。

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 returns Object 因为这是唯一的 class 我们可以确定 obj
  • set根本无法调用

我想这可能看起来很奇怪,因为如果您的 equalTo 最初声明为

public boolean equalTo(Box<?> other);

那你就可以调用了。但是,仅通过将 T 替换为 ?.

通配符不起作用
  • Box<?> 是 "a box of unknown".
  • Box<? extends String> 将是 "a box of things that are at least Strings"
  • Box<String>肯定是"a box of strings".
  • 因此,如果我们有一个 "box of unknown",我们会尝试在其中放入一个类型(例如字符串)。编译器不确定。它是一盒未知的。如果它实际上只接受整数怎么办?
  • 我们可以将 null 放入 "box of unknown" 吗?当然。
  • 我们 "get()" 从 "box of unknown" 得到什么?至少一个对象。

鉴于此,

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

它要求编译器忘记 <String> 并假设 box 是 "a box of unknown"。您看到的错误表明编译器无法再对参数进行类型检查。它无法检查给定类型是否属于未知类型(null 除外)。