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
在 OOP 中,您正在使用 多态性。所以在编译时,盒子对象的类型是 Box<?>
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 除外)。
我最近在学习 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
在 OOP 中,您正在使用 多态性。所以在编译时,盒子对象的类型是
Box<?>
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
returnsObject
因为这是唯一的 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 除外)。