乐趣区

关于spring:Java泛型中-和-extends-Object的异同分析

置信很多人和我一样,接触 Java 多年,却仍旧搞不清楚 Java 泛型中 <?><? extends Object>的类似和不同。然而,这应该是一个比拟高端大气上档次的 Question,在咱们进行深刻的探讨之前,有必要对 Java 泛型有一个根底的理解。具体请看上一篇文章!
重温 Java 泛型,带你更深刻地了解它,更好的应用它!

1. 泛型产生的背景

在 JDK5 中引入了泛型来打消编译时谬误和增强类型安全性。这种额定的类型安全性打消了某些用例中的强制转换,并使程序员可能编写泛型算法,这两种办法都能够生成更具可读性的代码。

例如,在 JDK5 之前,咱们必须应用强制转换来解决列表的元素。这反过来又产生了一类特定的运行时谬误:

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);
}

当初,咱们想解决两个问题:

  • 咱们须要一个显式转换来从 aList 中提取值——类型取决于左侧的变量类型(在本例中为Integer
  • 当咱们试图将 a_string 转换为 Integer 时,在第二次迭代中会呈现运行时谬误。

泛型填补了这个空白,代码如下:

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);
}

执行上述代码,编译器会通知咱们,无奈将 a_string 增加到 Integer 类型的 List 中,这比起在运行时才发现异常要好很多。
而且,不须要显式转换,因为编译器曾经晓得 iList 蕴含 Integer类型的数据。另外,因为主动拆箱的关系,咱们甚至不须要应用 Integer 类型,它的原始类型就足够了。

2. 泛型中的通配符

问号或通配符在泛型中用来示意未知类型。它能够有三种模式:

  • 无界通配符:List<?> 示意未知类型的列表
  • 上界通配符 :List<? extends Number> 示意 Number 或其子类型(如IntegerDouble)的列表
  • 下界通配符 :List<? super Integer> 示意Integer 或其超类型 NumberObject 的列表

因为 Object 是 Java 中所有类型的固有超类,所以咱们会认为它也能够示意未知类型。换句话说,List<?>List<Object> 能够达到雷同的目标。但事实并非如此。

来看看这两个办法:

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 + " ");    
    }     
}

给出一个 整数 的列表,比方:

List<Integer> li = Arrays.asList(1, 2, 3);

执行 printListObject(li) 不会编译,并且咱们将失去以下谬误:

The method printListObject(List<Object>) is not applicable for the arguments (List<Integer>)

而执行 printListWildCard(li) 将通过编译,并将 1 2 3 输入到控制台。

3. <?> 和 <? extends Object> 的相同之处

在下面的示例中,如果咱们将 printListWildCard 办法更改为:

public static void printListWildCard(List<? extends Object> list)

它的工作形式与 printListWildCard(List<?>)雷同。这是因为 Object 是 Java 所有对象的超类,基本上所有的货色都扩大了Object。因而,这个办法也会解决一个 Integer 类型的 List。

也就是说,<?> 和 <? extends Object> 在这个例子中是同一个意思。

尽管在大多数状况下,这是正确的,但也有一些区别。接下来咱们就来看看它们之间的差别。

4. <?> 和 <? extends Object> 的不同之处

可重构类型是指那些在编译时未被擦除的类型。换句话说,一个不可重构类型,运行时将比编译时表白的信息更少,因为其中一些信息会被擦除。

一般来说,参数化类型是不可从新定义的。比方 List<String>Map<Integer,String> 就不可从新定义。编译器会擦除它们的类型,并将它们别离视为列表和映射。

这个准则的惟一例外是无界通配符类型。也就是说,List<?> 以及 Map<?, ?> 是可重写的。

另外,List<? extends Object> 不可重写。尽管奥妙,但这是一个显著的区别。

不可重构的类型在某些状况下不能应用,例如在 instanceof 运算符或作为数组的元素。

所以,如果咱们的代码写成这样:

List someList = new ArrayList<>();
boolean instanceTest = someList instanceof List<?>

代码编译后,instanceTesttrue
然而,如果咱们在 List<? extends Object> 上应用 instanceof 运算符:

List anotherList = new ArrayList<>();
boolean instanceTest = anotherList instanceof List<? extends Object>;

那么第 2 行不编译。
相似地,在上面的代码片段中,第 1 行编译,但第 2 行不编译:

List<?>[] arrayOfList = new List<?>[1];
List<? extends Object>[] arrayOfAnotherList = new List<? extends Object>[1]

5. 结语

好了,文章到此就划上句号了,在本文中,咱们次要探讨了 <?> 和 <? extends Object> 的异同,尽管基本上是类似的,但两者在可变与否方面存在轻微差别。
如果你感觉文章还不错,记得关注公众号:锅外的大佬
刘一手的博客

退出移动版