java泛型探索——介绍篇
1. 泛型出现前后代码对比
先来看看泛型出现前,代码是这么写的:
List words = new ArrayList(); words.add("Hello "); words.add("worlds!"); String s = (String) words.get(0) + (String) words.get(1); System.out.println(s.equals("Hello worlds!"));
而泛型出现后无需做显式转换:
words.add("Hello "); words.add("worlds!"); String s = words.get(0) + words.get(1); System.out.println(s.equals("Hello worlds!"));
从本质上来说,以上代码编译为字节码后是一样的,对JVM而言没有区别。
2. 为什么有泛型?
泛型是什么?
泛型允许程序员编写代码时使用一些以后才指定的类型,允许在定义类和接口的时候使用类型参数,java泛型的参数只可以代表类,不能代表个别对象。
泛型的引入可以解决之前的集合类框架在使用过程中通常会出现的运行时刻类型错误[1]。
interface Iterator<E> { public boolean hasNext(); public E next(); public void remove(); ... }
泛型方法也可以哦:
class List { public static<T> List<T> toList(T[] arr) { List<T> list = new ArrayList<T>(); for (T elt : arr) list.add(elt); return list; } }
为什么有泛型?
1) 泛型可以隐式地执行类型转换,并且永远不会失败(cast-iron guarantee[2]):
List<String> words = new ArrayList<String>(); 由于泛型的类型擦除特性,所有添加到words中的变量,都会被转换为Object, 此时就需要泛型的类型转换特性,在执行words.get(i)时做强制转换。
2) 泛型由于类型擦除特性(后面的章节介绍),如List<Integer>,List<String>,List<Number>编译成字节码是一样的,
而不是每一个都有一个版本,保持着简洁性。
3) 最后也是最后要的一点:泛型的兼容性,可兼容jdk1.4以前的版本。从JVM的角度看,由于泛型代码编译成字节码与jdk1.4
之前的代码无任何差别,因此可以很容易的修改jdk1.4写的函数库中的某一个函数,而无需修改整个函数库。
3. 泛型通配符
首先以一个示例讲解为什么需要泛型通配符,如果我们定义addAll方法如下所示:
interface Collection<E> { ... public boolean addAll(Collection<E> c); ... } /** **测试代码 **/ List<Number> nums = new ArrayList<Number>(); List<Integer> ints = Arrays.asList(1,2,3); nums.addAll(ints); // 编译错误
如果我们想在List<Number>中添加List<Integer>怎么办呢?这是就需要使用通配符了。
interface Collection<E> { ... public boolean addAll(Collection<? extends E> c); ... } /** **测试代码 **/ List<Number> nums = new ArrayList<Number>(); List<Integer> ints = Arrays.asList(1,2,3); List<Double> dbls = Arrays.aslist(3.14,3.21); nums.addAll(ints); nums.addAll(dbls);
从上述代码可以看出,通配符的出现解决了泛型的子类问题:
List<Integer>与List<Double>都是List<? extends Number>的子类;而不是 List<Number>的子类。
最后,给出一个结论:当S是T的子类时,List<S>是List<?extends T>的子类;
当S是T的超类时,List<S>是List<? super T>的子类。此外,List<?>是List<? extends Object>的缩写。
接下来,我们分析通配符的重要的特性——put and get原则[2]。
对extends而言,List<? extends T>只能get不能add或put。
List<Integer> ints = new ArrayList<Integer>(); ints.add(1); ints.add(2); List<? extends Number> nums = ints;
System.out.println(nums.get(0)); // 编译成功 nums.add(3); // 编译错误 nums.add(3.14); // 编译错误
对于super而言,List<? super T>只能put或add,不能get。
List<Number> ints = new ArrayList<Number>(); ints.add(1); ints.add(2); List<? super Integer> nums = ints; nums.add(3);
凡是都有例外,对于extends来说,List<? extends Number> nums可以add null,即nums.add(null);对于super来说,
List<? super Integer> nums 可以get(取出)Object对象。
4. 参考文献
[1] http://www.infoq.com/cn/articles/cf-java-generics
[2] java Generics and Collections
java泛型探索——介绍篇的更多相关文章
- Java泛型学习---第二篇
泛型学习第一篇 1.泛型之擦拭法 泛型是一种类似"模板代码"的技术,不同语言的泛型实现方式不一定相同. Java语言的泛型实现方式是擦拭法(Type Erasure). 所谓擦拭法 ...
- java泛型探索——小特性
泛型特性(小篇幅) 1. 补充介绍一些常见的泛型特性: 类型参数T可以是recursive(类似递归性),它的边界可以是类型参数是自身的接口或类. 如我实现寻找最大值的方法,可以这么写: public ...
- Java泛型学习--第一篇
还是那句话,学习某个知识一定要想想为什么要学它,这方面的知识用来解决什么问题的,怎么用,并且要总结的体系化,不能散的到处都是,方便以后查看博客. 今天参考廖雪峰老师官网学习并总结下泛型廖老师官网 1. ...
- java泛型探索——泛型类
本文主要讨论一下如何声明泛型类,讨论的范围涉及构造函数.静态成员.内部类. 构造函数 泛型的类型参数首先声明在首部: public class Pair<T,U> { private fi ...
- Java——泛型
前言 一般的类和方法,使用的都是具体的类型:基本类型或者自定义的类.如果我们要编写出适用于多种类型的通用代码,那么肯定就不能使用具体的类型.前面我们介绍过多态,多态算是一种泛化机制,但是也会拘泥于继承 ...
- 转!!java泛型
介绍java泛型的一篇文章,通俗易懂! 原文地址:http://www.cnblogs.com/lwbqqyumidi/p/3837629.html 一. 泛型概念的提出(为什么需要泛型)? 首先,我 ...
- Java泛型介绍!!!
Java总结篇系列:Java泛型 转自:http://www.cnblogs.com/lwbqqyumidi/p/3837629.html 一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下 ...
- Java 泛型 介绍
为什么需要泛型? public class GenericTest { public static void main(String[] args) { List list = new ArrayLi ...
- java泛型(一)、泛型的基本介绍和使用
现在开始深入学习java的泛型了,以前一直只是在集合中简单的使用泛型,根本就不明白泛型的原理和作用.泛型在java中,是一个十分重要的特性,所以要好好的研究下. 泛 型的定义:泛型是JDK 1.5的一 ...
随机推荐
- empty 语句
empty 语句: 用来表明没有语句, 尽管JavaScript语法希望有语句会被执行. empty语句 用分号表示 (;) ,用来指明没有语句会被执行, 尽管此时JavaScript语法需要执行语句 ...
- SharePoint JavaScript 客户端对象使用视频教程
本次视频教程是为大家介绍如何使用SharePoint JavaScript客户端对象,包括对于站点.列表.文档库.列表项.文件夹.文件和附件等基本对象的操作,同时,为大家举几个简单的应用的例子,让大家 ...
- JavaEE开发之Spring中Bean的作用域、Init和Destroy方法以及Spring-EL表达式
上篇博客我们聊了<JavaEE开发之Spring中的依赖注入以及AOP>,本篇博客我们就来聊一下Spring框架中的Bean的作用域以及Bean的Init和Destroy方法,然后在聊一下 ...
- linux 下日常使用便利工具
Nautilus 你工作中有在GUI和命令行之间切来切去吗?当你总是要在命令行中输入你要进入的目录的时候,你有沮丧无奈过吗?如果有的话,那么,你一定要试下这个nautilus插件 —— nautilu ...
- Excel 按模板格式导出
最近遇到一个问题,就是导出数据的时候需要自定义的表头,如图 如果自己用代码写表头的话,可能会有点复杂,而且代码量很多,所以我就想了一个办法,直接在Excel里面把表头定义好,然后把数据写入Excel模 ...
- IOS开发创建开发证书及发布App应用(九)——等待审核(审核几种状态)
以下是App应用的几种状态,如果看不到英文,建议复制到网站翻译一下就行,意思差不多能明白的 以上整套流程是在2013年写的,可能有些地方已经不太一样了,只是给大家做一下参考,毕竟再怎么改大概流程还是差 ...
- 核心模块Path
核心模块Path 作用:用于帮助程序员来操作硬盘上的路径. 核心模块注意点:当引用核心模块的时候直接require('模块名'),不需要加任何路径或者后缀. Path中的常用API: dirname( ...
- 读 Zepto 源码之内部方法
数组方法 定义 var emptyArray = [] concat = emptyArray.concat filter = emptyArray.filter slice = emptyArray ...
- 易汇金在线支付接口实例。ecshop和shopex,shopnc,iwebshop下完美无错(最新)
最近为客户的一个在线商城做了一个易汇金在线支付的接口.跟大家分享一下. 1 首先可以模仿其他的接口,比如支付宝,财付通等的接口,构建模块功能文件和语言文件. 功能模块构建: /includes/mod ...
- 图文详解linux如何搭建lamp服务环境
企业网站建设必然离不开服务器运维,一个稳定高效的服务器环境是保证网站正常运行的重要前提.本文小编将会详细讲解Linux系统上如何搭建配置高效的lamp服务环境,并在lamp环境中搭建起企业自己的网站. ...