为什么需要泛型?
public class GenericTest { public static void main(String[] args) {
List list = new ArrayList();
list.add("qqyumidi");
list.add("corn");
list.add(100); for (int i = 0; i < list.size(); i++) {
String name = (String) list.get(i); //
System.out.println("name:" + name);
}
}
}
定义一个List类型的集合,先增加2个String类型的值,再增加1个Integer类型的值。这是完全允许的,因为此时list默认的元素类型为Object类型。在之后的循环中,由于忘记了之前在list中也加入了Integer类型的值,出现上面1中的错误。编译时正常,而运行时出现java.lang.ClassCastException。因此,此类错误在编码过程中不易被发现。
增加一个对象到集合中,集合不会记住此对象的类型。从集合中取出此对象时,该对象的编译时类型(表面类型)是Object类型,但其运行时类型(实际类型)是本身类型。因此,上面1处取出集合元素时需要人为地强制类型转换到具体的目标类型,容易出现java.lang.ClassCastException。
使用泛型达到这一目的——使集合能够记住集合内元素类型,只要编译时不出现问题,运行时就不会出现java.lang.ClassCastException。
什么是泛型?
泛型指参数化类型。将原来的具体类型参数化,类似于方法中的形参,类型定义成了参数形式即类型形参,然后在使用时传入具体的类型即类型实参。
使用泛型来修改上面的代码:
public class GenericTest { public static void main(String[] args) {
/*
List list = new ArrayList();
list.add("qqyumidi");
list.add("corn");
list.add(100);
*/ List<String> list = new ArrayList<String>();
list.add("qqyumidi");
list.add("corn");
//list.add(100); // 1 提示编译错误 for (int i = 0; i < list.size(); i++) {
String name = list.get(i); //
System.out.println("name:" + name);
}
}
}
采用泛型写法后,在上面1处增加1个Integer对象时会出现编译错误。List<String>中String是类型实参,直接限定了集合中只能含有String类型的元素,从而上面2处无需进行强制类型转换。也就是说,集合记住了元素的类型,编译器确认了它是String类型。
List接口的定义:
public interface List<E> extends Collection<E> { int size(); boolean isEmpty(); boolean contains(Object o); Iterator<E> iterator(); Object[] toArray(); <T> T[] toArray(T[] a); boolean add(E e); boolean remove(Object o); boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); boolean addAll(int index, Collection<? extends E> c); boolean removeAll(Collection<?> c); boolean retainAll(Collection<?> c); void clear(); boolean equals(Object o); int hashCode(); E get(int index); E set(int index, E element); void add(int index, E element); E remove(int index); int indexOf(Object o); int lastIndexOf(Object o); ListIterator<E> listIterator(); ListIterator<E> listIterator(int index); List<E> subList(int fromIndex, int toIndex);
}
List接口采用了泛型定义,<E>中的E表示类型形参,用于接收具体的类型实参。出现E的地方均表示接收外部相同的类型实参。
作为List接口的实现类,ArrayList的定义形式:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable { public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
} public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
} //...省略掉其他具体的定义过程 }
自定义泛型接口、泛型类和泛型方法
泛型类和泛型方法定义实例:
public class GenericTest { public static void main(String[] args) { Box<String> name = new Box<String>("corn");
System.out.println("name:" + name.getData());
} } class Box<T> { private T data; public Box() { } public Box(T data) {
this.data = data;
} public T getData() {
return data;
} }
常见的如T、E、K、V等形式的参数通常表示类型形参,接收外部传入的类型实参。<T>表示泛型方法,T是方法的返回类型。
对于传入的不同类型实参,相应生成的对象实例的类型是否一样?
public class GenericTest { public static void main(String[] args) { Box<String> name = new Box<String>("corn");
Box<Integer> age = new Box<Integer>(712); System.out.println("name class:" + name.getClass()); // com.qqyumidi.Box
System.out.println("age class:" + age.getClass()); // com.qqyumidi.Box
System.out.println(name.getClass() == age.getClass()); // true } }
在使用泛型类时,虽然传入了不同的类型实参,但没有生成不同的类型。可以传入不同类型实参的泛型类在内存上只有一个,即原来的基本类型(本实例中为Box)。因为泛型只是作用于编译阶段,通过编译的class文件是不包含任何泛型信息的。
类型通配符
根据上面的实例,Box<Number>和Box<Integer>实际上都是Box类型。Box<Number>和Box<Integer>是否可以看成具有父子关系的泛型类型呢?
public class GenericTest { public static void main(String[] args) { Box<Number> name = new Box<Number>(99);
Box<Integer> age = new Box<Integer>(712); getData(name); //The method getData(Box<Number>) in the type GenericTest is
//not applicable for the arguments (Box<Integer>)
getData(age); // } public static void getData(Box<Number> data){
System.out.println("data :" + data.getData());
} }
上面1处出现了错误提示信息:The method getData(Box<Number>) in the t ype GenericTest is not applicable for the arguments (Box<Integer>)。Box<Number>不能视为Box<Integer>的父类。
为什么?
public class GenericTest { public static void main(String[] args) { Box<Integer> a = new Box<Integer>(712);
Box<Number> b = a; //
Box<Float> f = new Box<Float>(3.14f);
b.setData(f); // } public static void getData(Box<Number> data) {
System.out.println("data :" + data.getData());
} } class Box<T> { private T data; public Box() { } public Box(T data) {
setData(data);
} public T getData() {
return data;
} public void setData(T data) {
this.data = data;
} }
上面1处和2处肯定会出现错误提示。使用反证法来说明:
假设Box<Number>可以视为Box<Integer>的父类,上面1处和2处将不会有错误提示。通过getData方法取出的数据到底是什么类型?Integer? Float? 还是Number?由于编程过程的顺序不可控性,在必要的时候判断类型,进行强制类型转换。这与泛型的理念矛盾。因此,Box<Number>不能视为Box<Integer>的父类。
类型通配符用来表示Box<Integer>和Box<Number>的父类的引用类型。
类型通配符使用?代替具体的类型实参。Box<?>是Box<Integer>和Box<Number>等所有Box<类型实参>的父类。
实例如下:
public class GenericTest { public static void main(String[] args) { Box<String> name = new Box<String>("corn");
Box<Integer> age = new Box<Integer>(712);
Box<Number> number = new Box<Number>(314); getData(name);
getData(age);
getData(number);
} public static void getData(Box<?> data) {
System.out.println("data :" + data.getData());
} }
限制类型实参只能是Number类及其子类,需要用到类型通配符上限:
public class GenericTest { public static void main(String[] args) { Box<String> name = new Box<String>("corn");
Box<Integer> age = new Box<Integer>(712);
Box<Number> number = new Box<Number>(314); getData(name);
getData(age);
getData(number); //getUpperNumberData(name); //
getUpperNumberData(age); //
getUpperNumberData(number); //
} public static void getData(Box<?> data) {
System.out.println("data :" + data.getData());
} public static void getUpperNumberData(Box<? extends Number> data){
System.out.println("data :" + data.getData());
} }
上面1处调用会出现错误,而上面2处和3处调用正常。
总之,类型通配符上限为Box<? extends Number>形式,类型通配符下限为Box<? super Number>形式,其含义与类型通配符上限相反。
注意:Java中没有泛型数组。
参考资料
Java 泛型 介绍的更多相关文章
-
Java泛型介绍!!!
Java总结篇系列:Java泛型 转自:http://www.cnblogs.com/lwbqqyumidi/p/3837629.html 一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下 ...
-
java泛型介绍
一.泛型初衷 Java集合不会知道我们需要用它来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要就具有很好的通用性.但这样做也带来两个问题: –集合对元素类型没有任何限制,这样可能引 ...
-
Java泛型介绍——HashMap总结
今天在编程中,需要使用到Hashmap来存储和传递数据,发现自己学习Java这么久,实际上对泛型依旧知之甚少,搜索整理了一下HashMap的使用. HashMap的声明初始化,因为泛型的原因,起两个参 ...
-
java泛型探索——介绍篇
1. 泛型出现前后代码对比 先来看看泛型出现前,代码是这么写的: List words = new ArrayList(); words.add("Hello "); words. ...
-
java泛型(一)、泛型的基本介绍和使用
现在开始深入学习java的泛型了,以前一直只是在集合中简单的使用泛型,根本就不明白泛型的原理和作用.泛型在java中,是一个十分重要的特性,所以要好好的研究下. 泛 型的定义:泛型是JDK 1.5的一 ...
-
Java泛型一:基本介绍和使用
原文地址http://blog.csdn.net/lonelyroamer/article/details/7864531 现在开始深入学习java的泛型了,以前一直只是在集合中简单的使用泛型,根本就 ...
-
java泛型 7 泛型的基本介绍和使用
现在开始深入学习Java的泛型了,以前一直只是在集合中简单的使用泛型,根本就不明白泛型的原理和作用.泛型在java中,是一个十分重要的特性,所以要好好的研究下. 一.泛型的基本概念 泛型的定义:泛型是 ...
-
Java泛型的历史
为什么Java泛型会有当前的缺陷? 之前的章节里已经说明了Java泛型擦除会导致的问题,C++和C#的泛型都是在运行时存在的,难道Java天然不支持“真正的泛型”吗? 事实上,在Java1.5在200 ...
-
java泛型基础
泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 这种参数类型可以用在类.接口和方法的创建中, 分别称为泛型类.泛型接口.泛型方法. Ja ...
随机推荐
-
SQL中CONVERT日期不同格式的转换用法
SQL中CONVERT日期不同格式的转换用法 格式: CONVERT(data_type,expression[,style]) 说明:此样式一般在时间类型(datetime,smalldatetim ...
-
PHP安全性
一.防sql注入 用户通过输入完整的字符,来和sql语句拼接成带有破坏性的sql语句,服务器执行该语句,造成破坏. 1使用mysql_real_escape_string()过滤数据,该方法在未来版本 ...
-
20151227感知机(perceptron)
1 感知机 1.1 感知机定义 感知机是一个二分类的线性分类模型,其生成一个分离超平面将实例的特征向量,输出为+1,-1.导入基于误分类的损失函数,利用梯度下降法对损失函数极小化,从而求得此超平面,该 ...
-
解决SQL数据库无法脱机的问题
数据库无法脱机:原理——有人在占用. 解决办法: select * from master.sys.sysprocesses where dbid=db_id('数据库名称') kill [id]
-
SQL SERVER 中PatIndex的用法个人理解
一般用法:PatIndex('%AAA%',‘BBBBBBBB’) 上句的意思是查找AAA在BBBBBBBB中的位置,从1开始计算,如果没有的话则返回0 其中%AAA%的用法和 SQL语句中like的 ...
-
php中const定义常量
const 常量 1.在定义时必须被初始值,2.前面不加任何修饰符3.变量名字母一般都大写4.常量可以被子类继承5.一个常量是属于一个类的,而不是某个对象的 作用:当某些值是固定不变的,就用const ...
-
如何使用vs将asp.net core项目添加容器支持并发布docker镜像到私有dockerhub和添加k8s/helm管理
这篇文章介绍一下,如何使用VS2017给asp.net core添加容器支持,并发布镜像到私有docker hub,然后用chart管理容器镜像的操作流程. 话不多说,just do it. 新建项目 ...
-
http升级https的时候,遇到一个问题
问题: Mixed Content: The page at 'https://api.xxxx.com/test' was loaded over HTTPS, but requested an i ...
-
[Swift]LeetCode782. 变为棋盘 | Transform to Chessboard
An N x N board contains only 0s and 1s. In each move, you can swap any 2 rows with each other, or an ...
-
Atitit 开发进度 开发效率提升解决方案
Atitit 开发进度 开发效率提升解决方案 1.1. 使用4gl语言 dsl语言尽可能 1 1.2. Ssd+高屏幕显示器,提升硬件 1 1.3. 汉字命名规范,可以大力提升可读性与效率 1 1. ...