我最近在学习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"));
由于
答案 0 :(得分:7)
Box<?> box = new Box<String>("abc");
box.put("xyz"); // error
String s = box.take(); // error
在OOP中,您正在使用多态。因此,在编译时,框对象的类型为Box<?>
Type<?>
这称为未知通配符。因为您不知道键入的Box类型,您只能从该对象读取,并且您只能使用读取为Object实例的对象。这就是收到box.put("xyz")
错误并收到String s = box.take()
错误的原因。
其次:
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
替换为?
即可使用通配符。