为什么说List<?>是type-safe而List不是type-safe的?

1、List<?>

  compiler看到了你使用了wildcard ?,那么相当于你对compiler说:“我不知道这个List里面的element的runtime-type是什么,如果我尝试对这个list或者list中取出来的object做一些type-specific的操作,你要给我一个compile-time-error来提醒我”。这样就导致了2个结果:

  1.1 list.get()返回类型为?,所以你只能用Object接收,Object足以确保type-safe,因为java中任何class都是Object的subclass。(当然,如果你非要使用类型强制转换,转换成什么阿猫阿狗的class,也没人拦得住你,对此只能说“编译器尽力了,你行你上啊”,反正ClassCastException什么的最有爱了)

  2.2 list.put()除了null以外,任何参数都不接收。这也足以确保list中类型的type-safe,要知道,java的泛型的implementation是基于ERASURE(擦除)的,举个具体的例子,LinkedList<E>的内部数据结构肯定是基于Node<E>,那么一个Node有2个field,E element和Node<E> next,而实际上在runtime环境中,LinkedList<String>中的Node并不是Node<String>,仅仅是Node,Node里面的element的类型也不是String,仅仅是Object,也就是说,compile-time的type-information都被抹除了(Quote: For backward-compatibility)。试想这么一个情景,Tom传了一个List<Dog>给Mike,Mike的interface是List<?>,Mike往list中放了一个Cat(假设compiler没有阻止Mike),然后Tom取出该List中所有的object并当成Dog使用(compiler会自动加上类型转换的代码——which is how java generics worked),然后Tom就悲剧地得到了一个ClassCastException——这就是为什么除了null其他参数都不接收的原因——阻止Mike随便放东西进去。

2、List

  raw-type就是这么个情况,相当于你对compiler说:“我并不在乎这个List里面的element的runtime-type是什么,不管我怎么操作这个list或者list中取出来的object,你都别管,实在看不过去就给我个warning就行了”。这种情况下:

  2.1 list.get()返回类型为Object,当然,也是type-safe的(如果你不强制转换的话)

  2.2 list.put()的参数类型为Object,也就是说,你爱往里面放什么object就放什么object,还是上面那个例子,就算Tom给Mike的是List<String>,但由于Mike的interface是List,所以Mike放个BigInteger甚至什么Cat、Dog,compiler都不会阻止Mike(但是,要知道,Mike是无法得知其他人会怎么使用这个List的,比如说Mike无法得知Tom相信编译器确保了list中的object都是String,但是由于Mike的raw-type interface,Tom就难免吃ClassCastException咯)

举个具体的例子:

class Dog {
public void bark() {
}
} class Cat {
public void meow() {
}
} public class Foo { public static void foo1(List<?> list) { // Java generics are implemented upon ERASURE, so the runtime-type of elements in List<?> and List are both Object.
if (list.isEmpty()) {
return;
}
Object o = list.get(0); // Type safe if you don't do DOWN-CAST
// list.add(new Dog()); // Won't compile
}
public static void foo2(List list) {
if (list.isEmpty()) {
return;
}
Object o = list.get(0); // Type safe if you don't do DOWN-CAST
list.add(new Cat()); //! Compiler won't stop you, just gives you a little complaint
}
public static void main(String[] args) {
List<Cat> cats = new LinkedList<Cat>();
cats.add(new Cat());
List<Dog> dogs = new LinkedList<Dog>();
dogs.add(new Dog()); foo1(cats); // relatively type-safe
foo2(dogs); // dangerous for (Cat cat : cats) { // Cast from Object to Cat
cat.meow();
} for (Dog dog : dogs) { //! Cast from Object to Dog, ClassCastException
dog.bark();
}
}
}

Java泛型:List<?>与List的区别的更多相关文章

  1. Java泛型中<? extends E>和<? super E>的区别

    这篇文章谈一谈Java泛型声明<? extends E>和<? super E>的作用和区别 <? extends E> <? extends E> 是 ...

  2. java 泛型E T ?的区别

    Java泛型中的标记符含义:  E - Element (在集合中使用,因为集合中存放的是元素) T - Type(Java 类) K - Key(键) V - Value(值) N - Number ...

  3. [转]JAVA泛型通配符T,E,K,V区别,T以及Class<T>,Class<?>的区别

    原文地址:https://www.jianshu.com/p/95f349258afb 1. 先解释下泛型概念 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被 ...

  4. JAVA泛型通配符T,E,K,V区别,T以及Class<T>,Class<?>的区别

    1. 先解释下泛型概念 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛 ...

  5. Java泛型中extends和super的区别?

    <? extends T>和<? super T>是Java泛型中的"通配符(Wildcards)"和"边界(Bounds)"的概念. ...

  6. 浅谈Java泛型之<? extends T>和<? super T>的区别

    关于Java泛型,这里我不想总结它是什么,这个百度一下一大堆解释,各种java的书籍中也有明确的定义,只要稍微看一下就能很快清楚.从泛型的英文名字Generic type也能看出,Generic普通. ...

  7. 初识java泛型

    1 协变数组类型(covariant array type) 数组的协变性: if A IS-A B then A[] IS-A B[] 也就是说,java中的数组兼容,一个类型的数组兼容他的子类类型 ...

  8. Java深度历险(五)——Java泛型

      作者 成富 发布于 2011年3月3日 | 注意:QCon全球软件开发大会(北京)2016年4月21-23日,了解更多详情!17 讨论 分享到:微博微信FacebookTwitter有道云笔记邮件 ...

  9. Java泛型-内部原理: 类型擦除以及类型擦除带来的问题

    一:Java泛型的实现方法:类型擦除 大家都知道,Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除.Java的泛型基本上都是在编译 ...

  10. java 深度探险 java 泛型

    Java泛型(generics)是JDK 5中引入的一个新特性,允许在定义类和接口的时候使用类型参数(type parameter).声明的类型参数在使用时用具体的类型来替换.泛型最主要的应用是在JD ...

随机推荐

  1. windows 10 专业版 激活

    参考文章:https://jingyan.baidu.com/article/c14654134b99de0bfcfc4c8c.html http://www.windowszj.com/news/2 ...

  2. java的Timer和TimerTask

    java中Timer类使用的方法是如下的: Timer timer = new Timer(); timer.schedule(new TimerTask() { public void run() ...

  3. 立个FLAG

    今天再次初步浏览了寒假生活: 三篇阅读笔记(人月神话,梦断代码,程序员修炼之道:从小工到专家),2月24日之前发表开发记账本软件,连续七天以上发表开发进度 学会使用GitHub,录制抖音(父母评价作品 ...

  4. JIT物料在途未清PO作为供给

    以下是本应在TEMP中处理,目前是写到FP_CHECK_SAP_DATA中 ----JIT物料在途未清PO作为供给 add by landor on 20180809 INSERT INTO SAP_ ...

  5. js中switch/case分支的值可以是变量或表达式

    在一些高级语言如C#中,switch分支的值只能是常量,而js中可以是变量或表达式: <!DOCTYPE html> <html lang="en"> &l ...

  6. 超详细 Nginx 极简教程

    什么是Nginx? Nginx (engine x) 是一款轻量级的Web 服务器 .反向代理服务器及电子邮件(IMAP/POP3)代理服务器. 什么是反向代理? 反向代理(Reverse Proxy ...

  7. jenkins搭建自动化部署平台(svn+tomcat重启)

    原文链接:http://blog.csdn.net/zjs40/article/details/51818322 jenkins 自动化部署和集成代码的平台 可以将Git svn 上的代码 check ...

  8. as3.0视频的快进有拖动条

    package com{ import flash.display.MovieClip; import flash.events.MouseEvent; import fl.video.FLVPlay ...

  9. 3. Longest Substring Without Repeating Characters (ASCII码128个,建立哈西表)

    Given a string, find the length of the longest substring without repeating characters. For example, ...

  10. ionic创建工程中遇到异常、错误及解决方法

    1. 创建工程——download failed ionic start myApp tabs 遇到如下错误 Downloading--Failed! Error:Timeout of 25000ms ...