Java中的泛型,本质上来说,就是是参数化类型,就是说所操作的数据类型被指定为一个参数,而不是确定的某种类型。这种数据类型可以用在类、接口和方法创建中。即泛型类、泛型接口、泛型方法。这样说可能不够生动,来举些例子说明一下。

例子一


我们通过 例子一 来简单看看泛型的应用场景。下面的情况中,没有使用泛型:

public class FanXingTest {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
Integer integer1 = 1;
arrayList.add(integer1);
//你记得这个元素的类型,那么没问题,通过index取出来
Integer integer2 =(Integer) arrayList.get(0);
System.out.println(integer2);
//假如你记错了arraylist里面存的类型了,这时编译器也不会报错,但是运行时会报错
String string =(String) arrayList.get(0);
System.out.println(string);
}
}

ArrayList里存放的是Object类。这样的好处是:通过对类型Object的引用来实现可以存放任意类(Java的多态机制),但也带来了缺点:需要进行强制类型转换。

可以看到,在没有使用泛型的情况下,你若记得 arrayList 里面存了什么类型的东西,那么最好不过了,可以直接通过index来取出来,并进行类型转换即可。

而假如你在不知道的情况下强制转换写错,编译器也不会提示错误,在运行时才会发现,会报一个错: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String 。实际情况中程序员不可能对每个元素都记得其类型,所以这样有明显的不合理性。

泛型的引入可以解决上述问题,先来看看针对上述情景使用泛型的情况:

public class FanXingTest {
public static void main(String[] args) {
ArrayList<Integer> arrayList = new ArrayList<Integer>();
Integer integer1 = 1;
arrayList.add(integer1);
//直接取出来,无需强制类型转换
Integer integer2 = arrayList.get(0);
System.out.println(integer2);
//编译器会检查出错误,根本不允许你把类型搞混了,下面两种情况编译器都直接提示不能类型转换
String string1 =(String) arrayList.get(0);
String string2 = arrayList.get(0);
}
}

上面的代码中我们使用了泛型,就是在创建 arrayList 的时候,就限定了里面存放的只能是 Integer 类型的数据,那么编译时就可以检查类型安全,所有的强制类型转换是自动和隐式的,提高代码重用率。我们在取出数据的时候,就无需进行强制类型转换,而且你把类型搞错,编译器会提示错误。

例子二


那么泛型是如何实现的呢?自己如何创建泛型类?我们通过 例子二 来看一看。先自己来创建一个泛型类,如下所示:

//自己新建一个泛型类:MyFan
class MyFan<T>{
private T t1;
//构造函数
public MyFan(T t1){
this.t1=t1;
}
//测试泛型的操作数据类型
public void showInfo(){
System.out.println("所操作数据类型是:"+t1.getClass().getName());
//通过反射机制,可以知道T的各种成员变量和方法的信息
Method m[]=t1.getClass().getDeclaredMethods();
for (int i = 0; i < m.length; i++) {
System.out.println(m[i].getName());
}
}
}

自己创建泛型类的时候,先假设这样一个处境:我希望我的这个类可以处理多种类型的数据。这就需要把类型"参数化",也就是当成一个"参数"传进来。所以定义类的时候,在类名的后面跟了一个<T>: class MyFan<T> ,这个T只是一个象征意义的符号,可以是任意符号。T 就是个类型"参数",我们之后可以传任意类型的类进来。上面的 showInfo() 方法中,我们可以利用反射机制,来得到传进来的这个数据类型是什么,也可以得到这个类里面有些个什么方法。接下来,我们利用我们自己创建的泛型类 MyFan<T> 来做个试验:

public class FanXingTest {
public static void main(String[] args) {
// TODO Auto-generated method stub MyFan<String> ft1=new MyFan<String>("黄黄");
ft1.showInfo();
MyFan<Integer> ft2=new MyFan<Integer>(1);
ft2.showInfo(); //输出两个实例对象的类型,结果是一样的
System.out.println(ft1.getClass());
System.out.println(ft2.getClass()); }
}

通过创建我们自己的泛型类的实例, ft1 里面的 t1 变量指向一个String类实例, ft2 里面的 t1 变量指向一个Integer类实例,那么上述程序的输出如下:

所操作数据类型是:java.lang.String...String的各个成员方法略去
所操作数据类型是:java.lang.Integer...Integer的各个成员方法略去
class MyFan
class MyFan

最后两行的输出,我们是想来通过 ft1/ft2.getClass() 来看看创建的这两个实例的类型会不会不一样,结果可以看到,两个都是属于 MyFan类的实例变量,并没有因为传入类型的不同而引起不同。

例子三


现在应该知道了泛型是如何运作的,我们在自己定义泛型类或者泛型接口的时候,<T>都是跟在类名的后面: class MyFan<T>{...} ,然后实例化的时候<T>也跟在类名的后边即可: MyFan<T> fan1 = new MyFan<T>()。

有些时候我们还会遇到更为复杂的情况,这几天我在工具类Collections里面,看到了sort方法,如下:

public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}

我们需要知道extends后面跟的类型表示泛型的上限(含自己),super表示的是泛型的下限(含自己)。如<T extends 类/接口>,表示T必须是该指定类/接口的子类(实现了某接口或继承自某类,都算"子类")。

我们来看 <T extends Comparable<? super T>> ,首先规定了T必须是 Comparable<? super T> 的一个子类,也就是说,T 必须是实现了 Comparable<? super T> 这个接口的。然后<? super T>表示Comparable<>中的类型下限为T(就是至少得是T的父类,包括T)!我们可以通过 GregorianCalendar 类和 Calendar 类来加深理解。先看下两个类的定义:

//Calendar类实现了Comparable<Calendar>接口
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar>{
...
@Override
public int compareTo(Calendar anotherCalendar) {
return compareTo(getMillisOf(anotherCalendar));
}
}
//GregorianCalendar类继承自Calendar类
public class GregorianCalendar extends Calendar{...}

那我们现在假设有个ArrayList,调用上面的sort方法:

ArrayList<GregorianCalendar> arrayList = new ArrayList<GregorianCalendar>();
Collections.sort(arrayList);

此时不会报错,因为相当于 <GregorianCalendar extends Comparable<Calendar>> 是成立的,我们结合两个类的定义可以知道:Calendar为GregorianCalendar 的父类,并且Calendar实现了Comparable<Calendar>,于是GregorianCalendar 也实现了Comparable<Calendar>接口。所以泛型的限制范围 <T extends Comparable<? super T>> 是满足的。接下来我们自己定义一个sort函数:

public static <T extends Comparable<T>> void sort(ArrayList<T> list) {
list.sort(null);
}

此时调用我们的sort函数 sort(new ArrayList<GregorianCalendar>()); ,编译器就会报错,因为GregorianCalendar类只是实现了Comparable<Calendar>接口,并没有实现Comparable<GregorianCalendar>接口。于是传入的参数不满足泛型的限制范围,所以会报错

自己的一些思考


先看看下面的Collections类里面的两个sort方法

public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}

注意到上面除了方法参数(比如List<T> list)之外,都是在返回类型(void)之前对方法参数类型进行了限定(比如static <T extends Comparable<? super T>> void)。

其实泛型就是”可有可无”,它的存在就是限制了你将要使用的参数类是什么范围内,extends/super分别是泛型的上下界(含)。就比如上面第一个sort,限定了<T extends Comparable<? super T>>,第二个就是没限定,任意的<T>;但是还有参数这块,第一个的参数就是之前限定好的参数T即可;第二个的参数呢,在Comparator<? super T>这里限定了一个下届,得是T的父类。

再往下说,为什么是<T extends Comparable<? super T>>而不是<T extends Comparable<T>>呢。这其实考虑到了子类若是继承自父类,而父类已经实现了Comparable接口,子类可以拿来用,所以就有<T extends Comparable<? super T>>的效果,举个例子就是上面的GregorianCalendar类和Calendar类,等效于子类GregorianCalendar也实现了Comparable接口。这时候若是<T extends Comparable<T>>就不合适了。

其实一般自己实现的Comparable接口的话,都是 class MyTest implements Comparable<MyTest>这种,估计也不会class MyTest implements Comparable<MyTest的父类>,上面的<T extends Comparable<? super T>>只是考虑到了比如ManmanTest继承自MyTest,那么ManmanTest也就实现了Comparable<MyTest>接口的情况。

Java中泛型的理解的更多相关文章

  1. Java中泛型 类型擦除

    转自:Java中泛型是类型擦除的 Java 泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,但有一点需要注意:Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类 ...

  2. java中ThrealLocal的理解

    目录 java中threadlocal的理解 一.threadlocal的生命周期和ThreadLocalMap的生命周期 二.ThreadLocal的作用 三.threadlocal示例 四.Inh ...

  3. java中threadlocal的理解

    [TOC] #java中threadlocal的理解##一.threadlocal的生命周期和ThreadLocalMap的生命周期可以吧TreadLocal看做是一个map来使用,只不过这个map是 ...

  4. 谈谈我对Java中CallBack的理解

    谈谈我对Java中CallBack的理解 http://www.cnblogs.com/codingmyworld/archive/2011/07/22/2113514.html CallBack是回 ...

  5. Java中泛型使用

    Java中泛型使用 泛型作用: 泛型:集合类添加对象不用强转 反射机制:将泛型固定的类的所有方法和成员全部显示出来 核心代码: ArrayList<Ls> ff=new ArrayList ...

  6. 沉淀再出发:关于java中的AQS理解

    沉淀再出发:关于java中的AQS理解 一.前言 在java中有很多锁结构都继承自AQS(AbstractQueuedSynchronizer)这个抽象类如果我们仔细了解可以发现AQS的作用是非常大的 ...

  7. Java中hashcode的理解

    Java中hashcode的理解 原文链接http://blog.csdn.net/chinayuan/article/details/3345559 怎样理解hashCode的作用: 以 java. ...

  8. Java中泛型在集合框架中的应用

    泛型是Java中的一个重要概念,上一篇文章我们说过,当元素存入集合时,集合会将元素转换为Object类型存储,当取出时也是按照Object取出的,所以用get方法取出时,我们会进行强制类型转换,并且通 ...

  9. Java 中泛型的全面解析(转)

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

随机推荐

  1. 在gem5的full system下运行 x86编译的测试程序 running gem5 on ubuntu in full system mode in x86

    背景 上篇博客写了如何在gem5的full system模式运行alpha的指令编译的程序,这篇博客讲述如何在gem5的full system模式运行x86指令集编译的程序,这两种方式非常类似. 首先 ...

  2. php提供service总结---wsdl篇

    越来越多的架构偏向于面向接口和面向服务的设计了,当我们把抽象的落地变为实际的时候,我们感觉到了代码的厚度.而当我们把具体的业务再进一步抽象,我们就能发现藏在细节深处的回馈. php可以提供servic ...

  3. TFS Services 集成Docker

    随着Docker的爆发,越来越多软件研发团体开始享用和受益于Docker系统体系带来的巨大好处.Docker的使用,除了减少软硬件成本的立竿见影效果,更是对软件生命周期过程开发.测试.生成部署和运维整 ...

  4. spring请求到达controller但响应404

    问题是这样的,前台发送请求的后台,后台的方法正常执行,将数据放在response.getWrite里,但在前台并没有展示数据.用浏览器的开发者工具看下请求,发现响应404. 最后网上查了查,sprin ...

  5. 结构-行为-样式-Angularjs-ngSanitize

    简单点,上代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> < ...

  6. django form表单验证

    一. django form表单验证引入 有时时候我们需要使用get,post,put等方式在前台HTML页面提交一些数据到后台处理例 ; <!DOCTYPE html> <html ...

  7. HTML5的文档结构

    HTML5的文档结构 HTML5简化了许多,它的设计遵循了3个原则:1.兼容性.2.实用性.3.通用访问性     1. header 元素     <header> 标签定义文档或者文档 ...

  8. Santa Claus and Tangerines

    Santa Claus and Tangerines 题目链接:http://codeforces.com/contest/752/problem/E 二分 显然直接求答案并不是很容易,于是我们将其转 ...

  9. jndi 与 jdbc

    现在开发中经常用到数据库的两种配置1 jdbc2  jndi 一般开发环境都会使用jdbc环境,自己弄配置方便.但是测试和生产环境一般都使用jndi方式.原因有:1   使用jndi方式配置,数据库的 ...

  10. linux xfce4普通用户 mount usb提示: Not authorized to perform operation

    问题:xfce4下,USB 硬盘能自动挂载并显示,但是普通用户操作时,提示:Not authorized to perform operation. 时间:20160928 os:gentoo + x ...