Java 泛型 介绍
为什么需要泛型?
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 ...
随机推荐
- Unable to locate package python-pip
原文:https://blog.csdn.net/yyinhai/article/details/53056973 Ubuntu下执行apt install python-pip得到如下错误提示: R ...
- GoLang函数参数的传递练习
春节买的GO方面的书,看了一次.现在撸一些代码,作为练习. // Copyright © 2019 NAME HERE <EMAIL ADDRESS> // // Licensed und ...
- context日志
class Program { static void Main(string[] args) { List<wolf_example> Listw; using (var ctx = n ...
- Windows Azure 部署 Windows 8 虚拟机
基本步骤其实很简单,主要有: 本地部署虚拟机 将虚拟机VHD上传至Azure 在Azure上根据VHD生成映像 利用映像生成虚拟机 下面我们开始: 1,本地部署虚拟机 首先我们需要在本地用 Hyper ...
- COOKIE和SESSION有什么区别?
1,位置--session 在服务器端,cookie 在客户端(浏览器)2,形式--session 默认被存在在服务器的一个文件里(session 可以放在 文件.数据库.或内存中都可以),cooki ...
- 清北合肥day1
题目: 1.给出一个由0,1组成的环 求最少多少次交换(任意两个位置)使得0,1靠在一起 n<=1000 2.两个数列,支持在第一个数列上区间+1,-1 每次花费为1 求a变成b的最小代价 n& ...
- ng2tree在ios中无法触发click
问题描述: 从其他页面跳转回ng2tree的页面时,无法触发节点的click事件 解决方案: 1. 在node_modules中搜索onNodeSeclected方法,修改click为touchend ...
- day4.字符串练习题
有变量 name = “alex leNb”,完成如下操作 1. 移除name变量对应的值两边的空格,并输出处理结果 print(name.strip()) 2. 移除name变量左边的’al’并输出 ...
- JAVA程序的基本结构
java程序(类)其实是一个静态方法库,或者是定义了一个数据类型.所以会遵循7种语法: 1.原始数据类型: ---整型:byte.short.int.long ---浮 ...
- HDU 1301-Jungle Roads【Kruscal】模板题
题目链接>>> 题目大意: 给出n个城市,接下来n行每一行对应该城市所能连接的城市的个数,城市的编号以及花费,现在求能连通整个城市所需要的最小花费. 解题分析: 最小生成树模板题,下 ...