1. Overview
1.概述
In this quick tutorial, we’ll see the similarities and differences between <?> and <? extends Object> in Java Generics.
在这个快速教程中,我们将看到<?>和<? extends Object>在Java泛型中的相似和不同。
However, this being an advanced topic, it’s imperative to get a basic understanding of the subject before we dive into the crux of the matter.
然而,作为一个高级话题,在我们深入探讨问题的核心之前,必须先对这个话题有一个基本的了解。
2. Background of Generics
2.仿制药的背景
Generics were introduced in JDK 5 to eliminate run-time errors and strengthen type safety. This extra type-safety eliminates casting in some use cases and empowers programmers to write generic algorithms, both of which can lead to more readable code.
JDK 5中引入了泛型,以消除运行时错误并加强类型安全。这种额外的类型安全消除了某些使用情况下的铸造,并使程序员有能力编写通用算法,这两种情况都可以使代码更加可读。
For example, pre-JDK 5, we’d have to work with the elements of a list using casting. This, in turn, created a certain class of runtime errors:
例如,在 JDK 5 之前,我们必须使用 casting 来处理列表中的元素。这反过来又产生了某类运行时错误。
List aList = new ArrayList();
aList.add(new Integer(1));
aList.add("a_string");
for (int i = 0; i < aList.size(); i++) {
Integer x = (Integer) aList.get(i);
}
Now, this code has two issues we’d like to address:
现在,这段代码有两个我们想要解决的问题。
- We need an explicit cast to extract values from aList – the type depends on the variable type on the left – Integer in this case
- We’ll get a runtime error on the second iteration when we’re trying to cast a_string to an Integer
Generics fill the role for us:
仿制药为我们填补了这一角色。
List<Integer> iList = new ArrayList<>();
iList.add(1);
iList.add("a_string"); // compile time error
for (int i = 0; i < iList.size(); i++) {
int x = iList.get(i);
}
The compiler will tell us that it’s not possible to add a_string to a List of type Integer, which is better than finding out at runtime.
编译器会告诉我们,不可能将a_string添加到List的Integer类型中,这比在运行时发现要好。
Moreover, no explicit casting is needed since the compiler already knows that iList holds Integers. Additionally, due to the magic of unboxing, we didn’t even need an Integer type, its primitive form is enough.
此外,由于编译器已经知道iList持有Integers,所以不需要明确的转换。此外,由于拆箱的魔力,我们甚至不需要一个Integer类型,它的原始形式就足够了。
3. Wildcards in Generics
3.通用语中的通配符
A question mark, or wildcard, is used in generics to represent an unknown type. It can have three forms:
问号,或通配符,在泛型中用来表示未知类型。它可以有三种形式。
- Unbounded Wildcards: List<?> represents a list of unknown type
- Upper Bounded Wildcards: List<? extends Number> represents a list of Number or its sub-types such as Integer and Double
- Lower Bounded Wildcards: List<? super Integer> represents a list of Integer or its super-types Number and Object
Now, since Object is the inherent super-type of all types in Java, we would be tempted to think that it can also represent an unknown type. In other words, List<?> and List<Object> could serve the same purpose. But they don’t.
现在,由于Object是Java中所有类型的固有超类型,我们会想它也可以代表一个未知的类型。换句话说,List<?>和List<Object>可以达到同样的目的。但它们并没有。
Let’s consider these two methods:
让我们考虑一下这两种方法。
public static void printListObject(List<Object> list) {
for (Object element : list) {
System.out.print(element + " ");
}
}
public static void printListWildCard(List<?> list) {
for (Object element: list) {
System.out.print(element + " ");
}
}
Given a list of Integers, say:
给出一个Integers的列表,例如。
List<Integer> li = Arrays.asList(1, 2, 3);
printListObject(li) will not compile, and we’ll get this error:
printListObject(li)将不会被编译,我们会得到这个错误。
The method printListObject(List<Object>) is not applicable for the arguments (List<Integer>)
Whereas printListWildCard(li) will compile and will output 1 2 3 to the console.
而printListWildCard(li)将被编译并将输出1 2 3到控制台。
4. <?> and <? extends Object> – the Similarities
4.<?>和<? extends Object>–相似之处
In the above example, if we change the method signature for printListWildCard to:
在上面的例子中,如果我们把printListWildCard的方法签名改为。
public static void printListWildCard(List<? extends Object> list)
It would function in the same way as printListWildCard(List<?> list) did. This is due to the fact that Object is a supertype of all Java objects, and basically everything extends Object. So, a List of Integers gets processed as well.
它的功能与printListWildCard(List<?> list)的功能相同。这是由于Object是所有Java对象的超类型,而且基本上所有的东西都扩展了Object。所以,一个List的Integers也会被处理。
In short, it means that ? and ? extends Object are synonymous in this example.
简而言之,它意味着?和? extends Object在这个例子中是同义的。
While in most cases that would hold true, but there are a few differences as well. Let’s look at them in the next section.
虽然在大多数情况下,这将是正确的,但也有一些区别。让我们在下一节看一下。
5. <?> and <? extends Object> – the Difference
5.<?>和<? extends Object>的区别
Reifiable types are those whose type is not erased at compile time. In other words, a non-reifiable type’s runtime representation will have less information than its compile-time counterpart, because some of it’ll get erased.
可重述类型是指其类型在编译时不会被删除的类型。换句话说,一个不可重述的类型的运行时表示将比它的编译时对应的信息少,因为其中一些信息会被删除。
As a general rule, parameterized types are not reifiable. This means List<String> and Map<Integer, String> are not reifiable. The compiler erases their type and treats them as a List and Map respectively.
一般来说,参数化的类型是不可重述的。这意味着List<String>和Map<Integer, String>是不可重述的。编译器会擦除它们的类型,并将它们分别作为List和Map处理。
The only exception to this rule is unbounded wildcard types. This means List<?> and Map<?,?> are reifiable.
这一规则的唯一例外是无界通配符类型。这意味着List<?>和Map<?,?>是可重复的。
On the other hand, List<? extends Object> is not reifiable. While subtle, this is a notable difference.
另一方面,List<? extends Object>是不可重述的。虽然很微妙,但这是一个值得注意的区别。
Non-reifiable types cannot be used in certain situations such as in an instanceof operator or as elements of an array.
在某些情况下,例如在instanceof操作符中或作为数组的元素,不能使用不可重新定义的类型。
So, if we write:
因此,如果我们写道。
List someList = new ArrayList<>();
boolean instanceTest = someList instanceof List<?>
This code compiles and instanceTest is true.
这段代码编译后,instanceTest为true。
But, if we use the instanceof operator on List<? extends Object>:
但是,如果我们在List<? extends Object>上使用instanceof操作符。
List anotherList = new ArrayList<>();
boolean instanceTest = anotherList instanceof List<? extends Object>;
then line 2 does not compile.
那么第2行就不能编译了。
Similarly, in the below snippet, line 1 compiles, but line 2 doesn’t:
同样,在下面的片段中,第1行可以编译,但第2行却不能。
List<?>[] arrayOfList = new List<?>[1];
List<? extends Object>[] arrayOfAnotherList = new List<? extends Object>[1]
6. Conclusion
6.结语
In this short tutorial, we saw the similarities and differences in <?> and <? extends Object>.
在这个简短的教程中,我们看到了<?>和<? extends Object>的相似和不同。
While mostly similar, there are subtle differences between the two in terms of their being reifiable or not.
虽然大部分情况下是相似的,但两者之间在是否可重新提供方面存在着微妙的差异。