我对 Java 中的通配符有一些错误的概念
I am having some wrong concept about wildcard in Java
我想了解如何在 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));
}
}
- 为什么第一个class不能编译而第二个可以?
- 当编译器看到通配符时会发生什么?把它变成对象?像第一个 class 中的
void foo(Object i)
或 void foo(List<Object> i)
.
- 第二个 class 中的类型推断是如何工作的?
在第一个示例中,您可以传递一个 List<String>
,因此如果您使用对象(l.get(0)
的结果)设置一个元素,您实际上无法安全地执行该操作。 有点短视。
在第二个示例中,l.get(0)
是一个 T,非常适合 l.set
、
解释这个的好教程https://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
...
It isn't safe to add arbitrary objects to it however:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
Since we don't know what the element type of c stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.
On the other hand, given a List, we can call get() and make use of the result. The result type is an unknown type, but we always know that it is an object. It is therefore safe to assign the result of get() to a variable of type Object or pass it as a parameter where the type Object is expected.
当我这样做时:
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
class 编译失败消息
The method set(int, capture#1-of ?) in the type List is not applicable for the arguments (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));
}
}
我想了解如何在 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));
}
}
- 为什么第一个class不能编译而第二个可以?
- 当编译器看到通配符时会发生什么?把它变成对象?像第一个 class 中的
void foo(Object i)
或void foo(List<Object> i)
. - 第二个 class 中的类型推断是如何工作的?
在第一个示例中,您可以传递一个 List<String>
,因此如果您使用对象(l.get(0)
的结果)设置一个元素,您实际上无法安全地执行该操作。 有点短视。
在第二个示例中,l.get(0)
是一个 T,非常适合 l.set
、
解释这个的好教程https://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
...
It isn't safe to add arbitrary objects to it however:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
Since we don't know what the element type of c stands for, we cannot add objects to it. The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don't know what type that is, we cannot pass anything in. The sole exception is null, which is a member of every type.
On the other hand, given a List, we can call get() and make use of the result. The result type is an unknown type, but we always know that it is an object. It is therefore safe to assign the result of get() to a variable of type Object or pass it as a parameter where the type Object is expected.
当我这样做时:
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
class 编译失败消息
The method set(int, capture#1-of ?) in the type List is not applicable for the arguments (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));
}
}