和我一起学Effective Java之泛型
泛型
不要在新代码中使用原始类型
泛型(generic):声明中具有一个或多个类型参数
原始类型(raw type):不带任何实际类型参数的泛型名称
格式:
类或接口的名称
<
对应于泛型形式类型参数的实际参数
>
如
List<String>
就是对应于List<E>
的实际参数为String
的参数化类型如与
List<E>
对应的原始类型是List
优点:
- 在编译时发现插入类型的错误(越早发现错误越好,编译时发现好于运行时发现)
- 不再需要手工转换类型
//JDK5之前的写法,使用的是原始类型
private static final List stringList = new ArrayList();
//有了泛型之后的写法,使用泛型
private static final List<String> stringList2 = new ArrayList<String>();
//JDK7 能将后面<>里的类型省略,被称为Diamond
private static final List<String> stringList3 = new ArrayList<>();
public static void main(String[] args) {
String str = "test";
Integer integer = 1;
stringList.add(str);
stringList.add(integer);//可通过编译,但之后报ClassCastException错误
stringList2.add(str);
// stringList2.add(integer);//无法通过编译
for(Iterator iterator = stringList.iterator();iterator.hasNext();){
String string = (String) iterator.next();
System.out.println(string);
}
for(Iterator iterator = stringList2.iterator();iterator.hasNext();){
String string = iterator.next();
System.out.println(string);
}
List
和List<Object>
之间的区别?
List
逃避了泛型检查,List<Object>
则是告知编译器,它能够持有任意类型的对象
无限制的通配符类型:
使用泛型,但不确定或者不关心实际的类型参数,可以用一个问号代替。如List<?>
泛型信息在运行时会被擦除
学习链接:
1.https://docs.oracle.com/javase/tutorial/java/generics/erasure.html
2.http://stackoverflow.com/questions/313584/what-is-the-concept-of-erasure-in-generics-in-java
下面通过一个小demo说明类型擦除
//类型擦除
List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();
System.out.println(stringList.getClass().toString());
System.out.println(integerList.getClass().toString());
System.out.println(stringList.getClass()==integerList.getClass());
integerList.add(100);
Method method = integerList.getClass().getMethod("add",Object.class);
method.invoke(integerList,"abc");
System.out.println(integerList);
运行结果:
一般不在代码中使用原始类型,除了两种例外情况(都是因为泛型信息在运行时会被擦除):
- 1.在类文字(class literals)中
如:
List.class,String[].class,int.class都合法
List<String>.class,List<String>.class都不合法
- 2.instanceof
if(o instanceof Set){ //原始类型(Raw Type)
Set<?> set = (Set<?>)o;//通配符类型(WildCard Type)
}
下面的表格是泛型相关的术语:
下面这张图很好的介绍了无限制通配符和其他泛型符号之间的关系:
消除非受检警告
始终在尽可能小的范围内使用SuppressWarnings注解
Java源码中的ArrayList类中有个toArray方法,其中就有强转的警告:
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
最好是将范围进一步缩小。将注解由整个方法到局部的变量声明上去。
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size){
@SuppressWarnings("unchecked")
T[] result = (T[]) Arrays.copyOf(elementData, size, a.getClass());
return result;
}
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
列表优于数组
- 数组是协变的(covariant),泛型则是不可变的
Object[] objectArray = new String[1];
List<Object> objectList = new ArrayList<String>();//无法通过编译 imcompatible types
// String类是Object类的子类
//String[]是Object[]的子类
//而List<String>并不是List<String>的子类型
- 数组是具体化的(reified),在运行时才知道并检查它们的元素类型约束。而泛型通过擦除来实现的。泛型只在编译时强化类型信息,并在运行时擦除它们的元素类型信息。擦除就是使泛型可以与没有使用泛型的代码可以互用。
Object[] objectArray = new String[1];
List<String> objectList = new ArrayList<String>();
objectArray[0] = 3;//可通过编译,运行时报错
// objectList.add(1);//编译时报错
数组和泛型不能很好地混合使用。可用列表代替数组。
总结:数组是协变且可具体化的,泛型是不可变的且可被擦除的。-->数组提供了运行时类型安全而编译时类型不安全。而泛型反之。
优先考虑泛型
泛型相比于Object的优点:
- 不需要强制类型转换
- 编译时类型安全
public class SomeClazz<T> {
public Object dosthWithObj(Object obj){
return obj;
}
public T dosthWithT(T t){
return t;
}
public static void main(String[] args) {
SomeClazz<Foo> someClazz = new SomeClazz<Foo>();
Foo foo = new Foo();
Foo foo1 = (Foo) someClazz.dosthWithObj(foo);
Foo foo2 = someClazz.dosthWithT(foo);
}
}
public class Stack<E> {
private E [] elements;
private static final int MAX_SIZE = 16;
private int size = 0;
@SuppressWarnings("unchecked")
public Stack(){
elements = (E[]) new Object[MAX_SIZE];
}
public void push(E e){
ensureSize();
elements[size++]=e;
}
public E pop(){
if(size==0)
throw new EmptyStackException();
E e = elements[--size];
elements[size]=null;
return e;
}
private void ensureSize() {
if(size==elements.length){
elements= Arrays.copyOf(elements,2*size+1);
}
}
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
for(int i =0;i<50;i++){
stack.push(i);
}
for(int i = 0;i<10;i++){
System.out.println(i+": "+stack.pop());
}
}
}
class EmptyStackException extends RuntimeException{
}
前面曾鼓励优先使用列表而不是数组。并不意味着所有的泛型中都要使用列表。况且Java并不是生来就支持列表的。
每个类型都是它自身的子类型。
如有 SomeClazz<E extends Number>
SomeClazz<Number>是合法的
优先考虑泛型方法
方法可以考虑泛型化,特别是静态工具方法。
泛型方法语法:
方法修饰语 泛型 返回值 方法名()
public static <T> T foo(T args);
/**
* 使用泛型方法
* 返回两个集合的联合
* @param s1
* @param s2
* @param <E>
* @return
*
* 局限:两个参数和返回的结果的类型必须全部相同
* 解决方法:使用有限制的通配符
*/
public static <E> Set<E> unionGeneric(Set<E> s1,Set<E> s2){
Set<E> result = new HashSet<>(s1);
result.addAll(s2);
return result;
}
public static <K,V> Map<K,V> newHashMap(){
return new HashMap<K,V>();
}
泛型单例工厂:
public interface UnaryFunction<T>{
T apply(T arg);
}
private static UnaryFunction<Object> IDENTITY_FUNCTION =
new UnaryFunction<Object>() {
@Override
public Object apply(Object arg) {
return arg;
}
};
@SuppressWarnings("unchecked")
public static <T> UnaryFunction<T> identityFunction(){
return (UnaryFunction<T>) IDENTITY_FUNCTION;
}
/**
* 每次都要创建一个,很浪费,而且它是无状态的.
* 泛型被具体化了,每个类型都需要一个恒等函数,但是它们被擦除后,就只需要一个泛型单例了.
* @param <T>
* @return
*/
public static <T> UnaryFunction<T> identityFunction2(){
return new
UnaryFunction<T>() {
@Override
public T apply(T arg) {
return arg;
}
};
}
递归类型限制:
通过某个包含该类型参数本身的表达式来限制类型参数
<T extends Comparable<T>>//针对可以与自身进行比较的每个类型T
利用有限制通配符来提升API的灵活性
参数化类型是不可变的。
虽然String类是Object类的子类,但是List<String>和List<Object>无关
/**
* 栈的实现
* @param <E>
* API:
* public Stack();
* public void push(E e);
* public E pop();
* public boolean isEmpty();
*
* 新增API:
* before:
* public void pushAll(Iterable<E> i);
* public void popAll(Collection<E> c);
* after:
* 使用有限制的通配符类型(bounded wildcard type)
* public void pushAll(Iterable<? extends E> i);
* public void popAll(Collection<? super E> c);
*
*/
class Stack<E>{
private E [] elements;
private static final int INIT_CAPABILITY = 16;
private int size = 0;
@SuppressWarnings("unchecked")
public Stack(){
elements = (E[]) new Object [INIT_CAPABILITY];
}
public void push(E e){
checkCapability();
elements[size++]=e;
}
public E pop(){
if(size==0)
throw new RuntimeException("Empty Stack");
E e = elements[--size];
elements[size]=null;
return e;
}
private void checkCapability() {
if(size==elements.length)
elements = Arrays.copyOf(elements,2*elements.length-1);
}
public boolean isEmpty(){
return size==0;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
String start = super.toString();
sb.append(start);
for(int i = 0 ;i<size;i++){
String s = " ["+elements[i]+"]";
sb.append(s);
}
return sb.toString();
}
//before
// public void pushAll(Iterable<E> i){
// for(E e:i){
// push(e);
// }
// }
// public void popAll(Collection<E> c){
// while (!isEmpty()){
// c.add(pop());
// }
// }
//after
public void pushAll(Iterable<? extends E> i){
for(E e:i){
push(e);
}
}
public void popAll(Collection<? super E> c){
while(!isEmpty()){
c.add(pop());
}
}
Stack<Number> stack= new Stack<>();
Iterable<Integer> integers = Arrays.asList(1,2,3,4,5);
Collection<Object> objectCollection = new LinkedList<>();
//before
// stack.pushAll(integers);//参数类型不对
// stack.popAll(objectCollection);//参数类型不对
//after
stack.pushAll(integers);
System.out.println(stack);
stack.popAll(objectCollection);
System.out.println(stack);
从上面的Demo中我们知道,Java中提供了有限制的通配符类型
来提高API的灵活性。
如
Collection<? extends E>
Collection<? super E>
一般在表示生产者或消费者的输入参数上使用通配符类型。
PECS:Producer-extends Consumer-super
------------------
* 参数化类型 通配符类型
* T生产者 extends
* T消费者 super
* ------------------
和我一起学Effective Java之泛型的更多相关文章
- 和我一起学Effective Java之创建和销毁对象
前言 主要学习创建和销毁对象: 1.何时以及如何创建对象 2.何时以及如何避免创建对象 3.如何确保它们能够适时地销毁 4.如何管理对象销毁之前必须进行的清理动作 正文 一.用静态工厂方法代替构造器 ...
- 和我一起学Effective Java之类和接口
类和接口 使类和成员的可访问性最小 信息隐藏(information hiding)/封装(encapsulation):隐藏模块内部数据和其他实现细节,通过API和其他模块通信,不知道其他模块的内部 ...
- Effective java -- 4 泛型
第二十三条:请不要在代码中使用原生态类型就是像Set这种待泛型的,就把泛型明确写出来. 第二十四条:消除非受检警告就是Set<String> sets = new HashSet();这种 ...
- Effective Java 第三版——29. 优先考虑泛型
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- Effective Java 第三版——32.合理地结合泛型和可变参数
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- effective java笔记之java服务提供者框架
博主是一名苦逼的大四实习生,现在java从业人员越来越多,面对的竞争越来越大,还没走出校园,就TM可能面临失业,而且对那些增删改查的业务毫无兴趣,于是决定提升自己,在实习期间的时间还是很充裕的,期间自 ...
- Effective java笔记(二),所有对象的通用方法
Object类的所有非final方法(equals.hashCode.toString.clone.finalize)都要遵守通用约定(general contract),否则其它依赖于这些约定的类( ...
- 《Effective java》-----读书笔记
2015年进步很小,看的书也不是很多,感觉自己都要废了,2016是沉淀的一年,在这一年中要不断学习.看书,努力提升自己!预计在2016年要看12本书,主要涉及java基础.Spring研究.java并 ...
- 《Effective Java》学习笔记——积累和激励
从一个实际案例说起 国庆长假前一个礼拜,老大给我分配了这么一个bug,就是打印出来的报表数量为整数的,有的带小数位,有的不带,毫无规律. 根据短短的两个多月的工作经验以及猜测,最终把范围缩小到以下这段 ...
随机推荐
- 429. N叉树的层序遍历
429. N叉树的层序遍历 题意 给定一个 N 叉树,返回其节点值的层序遍历. (即从左到右,逐层遍历). 解题思路 和二叉树的层次遍历的思想一样: 实现 class Solution(object) ...
- Unity 4.0 中的新动画系统——MecAnim
分享一个文档资料,关于动画系统的,版本应该很老了,但是有借鉴意义的: Unity 4.0 已于 2012 年 11 月 15 日正式发布,Unity 每一次版本的提升,都给游戏开发者带来惊喜,这一次也 ...
- Django的使用规则
ORM应该算是Python的一大特色,功能类似于Mybatis或hibernate,但是显示要强大更多 一:terminal下命令行的使用 1.创建一个Project django-admin sta ...
- UEditor实现前后端分离时单图上传
首先,需要下载部署ueditor相关代码,可以参考一篇简单的博客,这里不再赘述: 环境搭建好后,我们先简单使用一下,启动http://web.yucong.com:8080/ueditor/index ...
- ALSA学习资料
一.内核文档 Linux Sound Subsystem Documentation 二.一些API 1.snd_pcm_period_elapsed 2.snd_pcm_lib_buffer_by ...
- java判断传进来的是否是图片
public static void main(String[] args) throws IOException { String filePath = "C:\\Users\\80975 ...
- 阿里云ecs 增加虚拟网卡
cd /etc/sysconfig/network-scripts/ #进入网卡目录 cp ifcfg-eth0 ifcfg-eth0:1 # 复制出ifcfg-eth0:1虚拟网卡 vi i ...
- iOS开发-模板方法模式
模板方法模式定义一个操作中的算法的骨架,而将步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤.模板方法模式是由子类决定实现算法中的步骤,工厂方法由子类决定实现哪一 ...
- 深入理解JVM(一)——JVM内存模型
JVM内存模型 Java虚拟机(Java Virtual Machine=JVM)的内存空间分为五个部分,分别是: 1. 程序计数器 2. Java虚拟机栈 3. 本地方法栈 4. 堆 5. 方法区. ...
- docker 安装hadoop
上一篇文章介绍了一些docker的基本命令,这篇文章来安装一个HADOOP 一.下载hadoop镜像 @~/git/github/docker-ambari (master)$ docker pull ...