一、什么是泛型

  本质而言,泛型指的是参数化的类型。参数化的类型的重要性是:它能让你创建类、接口和方法,由它们操作的数据类型被指定为一个参数。操作参数化类型的类、接口或方法被称为泛型,如泛型类或泛型方法。着重理解的是,通过对Object类型的引用,Java总是可以创建一般化的类、接口和方法,因为Object是所有其他类的超类,一个Object引用可以是任何类型的对象,因此,在泛型出现以前的代码,一般化的类、接口和方法是使用Object引用来操作不同类型的对象(可不可以说是多态?),这样做的问题是不能保证类型安全。泛型添加了它们正缺乏的类型安全,同时还简化了过程,因为不需要显示地在Object和实际操作的数据类型之间进行强制转换。利用泛型,所有的强制转换都是自动和隐含的,因此,泛型扩展了代码复用的能力,而且既安全又方便。

下面是一个简单的泛型例子:

  

 package com.hujianjie.demo;

 //一个简单的泛型类
class Gen<T> {
T ob; Gen(T o) {
this.ob = o;
} T getOb() {
return ob;
} void showType() {
System.out.println("Type of T is" + ob.getClass().getName());
}
} public class GenDemo {
public static void main(String args[]) {
// 定义一个Integer类型
Gen<Integer> iob;
iob = new Gen<Integer>(88);//此处的<Integer>不能省,因为定义的类型是Integer类型,所以new返回的引用也必须是Integer,否则就会发生编译错误,也即泛型安全
iob.showType();
System.out.println("value:" + iob.getOb());
// 定义一个String 类型
Gen<String> sob;
sob = new Gen<String>("Generics Test!");
sob.showType();
System.out.println("value:" + sob.getOb());
} }

首先注意,类型Integer被放在Gen后面的一对尖括号内,在本例中,Integer作为类型变元被传递给Gen的类型参数T,这就创建了Gen的一个版本,在该版本中,所有对T的引用都被转换为对Integer的引用,因此,对于这个声明,ob成为Integer类型,且getob()的返回类型也是Integer类型。String类型也是类似分析。特别主意Java编译器并不创建实际的不同Gen版本或者任何其他的泛型类,尽管将其想象成这样是有帮助理解的,但实际上并不是这样发生的。相反,编译器删除所有的泛型类型信息,并进行必要的强制转换,使代码在行为上就好像创建了Gen的一个特定版本一样,因此,程序中只实际存在一个Gen版本。删除泛型类型信息的过程称为擦拭(erasure)。

二、泛型的特点。

  1、泛型只使用对象。声明泛型类型的一个实例时,传递给类型参数类型变元必须是类类型,不能使用基本类型,如int或char。

  2、泛型类型的差异基于类型变元。理解泛型类型的关键在于:对某种特定版本的泛型类型的引用,与对同一种泛型类型的另一种版本的引用是类型不兼容的。例如:上面代码中的 iob=sob 是错误的,虽然iob和sob都属于Gen<T>类型,由于它们的类型参数不同,它们引用的也是不同的类型。这就是泛型添加的类型安全和错误预防的原理所在。

  3、泛型能在编译时捕捉类型不匹配错误,因此能够创建类型安全的代码。通过泛型,原来的运行时错误现在成了编译错误,这是一个重要的提高。

三、泛型的一般形式

  声明泛型的语法: class class-name<type-param-list>{ . . . }

  声明一个泛型引用的语法: class-name<type-arg-list> var-name = new class-name <type-arg-list>(cons-arg-list);

四、泛型的类型

  1、有界类型。前面简单的例子中,类型参数可以替换为任意的类类型,多数情况下这一点是好的,但是有时候需要对可以传递给类型参数的类型做出限制,于是出现了有界类型。在指定一个类型参数的时候,可以创建一个上界,声明所有的类型变元都必须从超类派生,实现方法是通过在指定类型参数时使用一个extends子句,如下所示:

  < T extends superclass >

这行代码规定T只能由superclass或它的子类来替换。因此,superclass 提供了一个包括本身在内的上限。

除了用类类型作为有界之外,还可以使用接口类型,实际上,可以将多个接口指定为界,而且,界可以包括一个类类型和多个接口,此时,必须首先指定类类型,当界包含一个接口类型时,只有实现这个接口的类型变元才是合法的,若指定的界包含一个类和一个或者多个接口,则应使用 & 运算符连接它们。例如:

  class Gen < T extends MyClass & MyInterface > { . . . }

其中, T由称为MyClass 的类和称为MyInterface的接口界定,因此,任何传递给T的类型变元,都必须是MyClass 的一个子类,且实现MyInterface。

  2、通配符变元。通配符变元用 “?”指定,它代表一个未知类型

 package com.hujianjie.demo;

 /*
* 定义一个有界泛型类
*/
class Stats<T extends Number>{
T[] nums;
Stats(T [] o){
nums=o;
}
double average(){
double sum = 0.0;
for(int i =0;i<nums.length;i++){
sum+=nums[i].doubleValue();
}
return sum/nums.length;
}
/*
* 这里使用通配符变元,如果是Stats<T>,两个比较的对象类型必须一致,否则,
* 会出现泛型安全错误,所以使用通配符,在不同类型间进行比较
*/
boolean sameAvg(Stats<?> ob){
if(this.average()==ob.average())
return true;
return false;
}
} public class WildCardDemo { /**
* @param args
*/
public static void main(String[] args) { Integer inums[]={1,2,3,4,5};
Stats<Integer> iob = new Stats<Integer>(inums);
System.out.println("iob average is :"+iob.average());
Double dnums[]={1.1,2.2,3.3,4.4,5.5};
Stats<Double> dob = new Stats<Double>(dnums);
System.out.println("dob average is :"+dob.average());
Float fnums[]={1.0f,2.0f,3.0f,4.0f,5.0f};
Stats<Float> fob = new Stats<Float>(fnums);
System.out.println("dob average is :"+fob.average());
if(iob.sameAvg(dob))
System.out.println("iob and dob are the same!");
else
System.out.println("iob and dob are not the same!");
if(fob.sameAvg(iob))
System.out.println("iob and fob are the same!");
else
System.out.println("iob and fob are not the same!");
} }

  3、有界通配符。通配符变元的界定方式与类型参数大体相同,当创建一个操作与类层次的泛型时,有界通配符尤其重要。一个有界通配符可以为类型变元指定一个上界或一个下界。如:在坐标系里,二维、三维、四维坐标,如果要限定在一个三维坐标中,则可以使用如下方式:showXYZ(Coords< ? extends ThreeD> ob).

  4、泛型构造函数。构造函数也可以是泛型,即使它们的类也不是泛型。

  5、泛型接口。泛型接口的指定方法与泛型类相似。

 package com.hujianjie.demo;
/*
* 定义一个泛型上界接口,所传对象必须实现Comparable接口,可以比较大小
*/
interface MinMax<T extends Comparable<T> >{
T min();
T max();
}
/*
* 定义实现接口类时注意:MyClass声明参数类型 T 后要将它传递给MinMax,因为MinMax
* 需要一个实现Comparable的类型,实现类必须指定同样的界
* 此界一旦建立,就无需在implements字句中再次指定。
* class MyClass <T extends Comparable<T> > implements MinMax< T extends Comparable<T> >
* 这样指定是错误的
*/
class MyClass <T extends Comparable<T> > implements MinMax<T>{
T [] array;
MyClass (T []o){
array=o;
}
public T min(){
T minValue =array[0];
for(int i=0;i<array.length;i++){
if(minValue.compareTo(array[i])>0)
minValue=array[i];
}
return minValue;
}
public T max(){
T maxValue = array[0];
for(int i=0;i<array.length;i++){
if(maxValue.compareTo(array[i])<0){
maxValue=array[i];
}
} return maxValue; }
} public class GenIFDemo { /**
* @param args
*/
public static void main(String[] args) {
Integer inums[]={3,6,2,8,7};
Character chs[]={'b','r','c','d'};
MyClass<Integer> iob = new MyClass<Integer>(inums);
MyClass<Character> cob = new MyClass<Character>(chs);
System.out.println("Max values in inums is "+iob.max());
System.out.println("Min values in inums is "+iob.min());
System.out.println("Max values in chs is "+cob.max());
System.out.println("Min values in chs is "+cob.min());
} }

注意:如果一个类实现了一个泛型接口,则此类必须也是泛型,或者至少它接受传递给接口的类型参数。如果MyClass是一个没有引用泛型的类,只是实现了泛型接口如下声明,编译器会报错。

 class MyClass implements MinMax< T >{ . . . }

另外,如果上面的情况稍作改变就不同,如果类实现一个泛型接口的特定类型,则此实现类不需要是泛型的

 class MyClass implements MinMax< Integer>{ . . . }

  6、泛型类层次。和非泛型类一样,泛型类也可以是类层次中的一部分,因此,泛型类可以作为超类或子类。泛型类层次与非泛型类层次的关键区别在于:泛型类层次中,全部子类必须将泛型超类需要的类型变元沿层次向上传递。这与构造函数变元必须沿层次向上传递的方式类似。

 package com.hujianjie.demo;
class Gen <T>{
T ob;
Gen (T o){
ob =o;
}
T getOb(){
return ob;
}
}
class Gen2<T> extends Gen<T>{
Gen2(T o){
super(o);
}
} public class HierDemo { /**
* @param args
*/
public static void main(String[] args) {
Gen<Integer> iob = new Gen<Integer>(88);
Gen2<Integer> iob2 = new Gen2<Integer>(99);
Gen2<String> str2 = new Gen2<String>("Generics Test");
if(iob instanceof Gen<?>)
System.out.println("iob is instance of Gen");
//iob 向下转型,将不是Gen2对象的类型
if(iob instanceof Gen2<?>)
System.out.println("iob is instance of Gen2");
if(iob2 instanceof Gen<?>)
System.out.println("iob2 is instance of Gen");
if(iob2 instanceof Gen2<?>)
System.out.println("iob2 is instance of Gen2");
if(str2 instanceof Gen<?>)
System.out.println("str2 is instance of Gen");
if(str2 instanceof Gen2<?>)
System.out.println("str2 is instance of Gen2");
/*
* 以下这些行不能被编译的原因是:它们试图比较iob2与Gen2的一个特定
* 类型,本例中是Gen2<Integer>.记住,在运行时无法得到泛型类型信息,因此
* 对instanceof来说,无法判断iob2是否为Gen2<Integer>的一个实例
*/
// if(iob2 instanceof Gen2<Integer>)
// System.out.println("str2 is instance of Gen2"); } }

结果:

 iob is instance of Gen
iob2 is instance of Gen
iob2 is instance of Gen2
str2 is instance of Gen
str2 is instance of Gen2

五、泛型擦拭

  大体而言,擦拭的工作方式是这样的:当编译Java代码时,全部泛型类型信息被移去(擦拭)。这意味着使用它们的界定类型来替换类型参数,如果没有显式地指定界,则界定类型是Object,然后运用适当得强制转换(由类型变元决定),以维持与类型变元指定的类型的兼容。编译器也会强制这种类型兼容。对泛型来说,这种方法意味着在运行时不存在类型参数,它们仅是一种源代码机制。在编译时所有的类型参数都被擦拭掉了,在运行时,只有原始类型实际存在。

Java 泛型 详解的更多相关文章

  1. java 泛型详解(普通泛型、 通配符、 泛型接口)

    java 泛型详解(普通泛型. 通配符. 泛型接口) JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能---- ...

  2. java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一

    对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 1. 概述 泛型在 ...

  3. Java泛型详解(转)

    文章转自  importNew:Java 泛型详解 引言 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用.本文我们将从零开始来看一下Java泛型的设计,将会涉及到通配符处理 ...

  4. 【转】java 泛型详解

    java 泛型详解 对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 ...

  5. 【转载】Java泛型详解

    [转载]http://www.importnew.com/24029.html 对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考 ...

  6. java基础(十二 )-----Java泛型详解

    本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...

  7. Java基础11:Java泛型详解

    本文对java的泛型的概念和使用做了详尽的介绍. 本文参考https://blog.csdn.net/s10461/article/details/53941091 具体代码在我的GitHub中可以找 ...

  8. java 泛型详解-绝对是对泛型方法讲解

    Reference:  http://blog.csdn.net/s10461/article/details/53941091 1. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模 ...

  9. Java泛型详解,史上最全图文详解!

    泛型在java中有很重要的地位,无论是开源框架还是JDK源码都能看到它. 毫不夸张的说,泛型是通用设计上必不可少的元素,所以真正理解与正确使用泛型,是一门必修课. 一:泛型本质 Java 泛型(gen ...

  10. 笔记-java泛型详解

    首先,先说明一下,java泛型文章的出处:http://www.cnblogs.com/lzq198754/p/5780426.html 作为学习笔记保存. 1.为什么需要泛型 泛型在Java中有很重 ...

随机推荐

  1. 【转】如何搭建IPv6测试你的APP

    IPv6的简介 IPv4 和 IPv6的区别就是 IP 地址前者是 .(dot)分割,后者是以 :(冒号)分割的(更多详细信息自行搜索). PS:在使用 IPv6 的热点时候,记得手机开 飞行模式 哦 ...

  2. 奇怪的bug:javascript不执行

    背景:有人想要个简单的js效果,点击某个菜单,其他菜单收起. 说了下思路,结果~~ 只好直接写了一个,代码如下: <!DOCTYPE html> <html> <head ...

  3. 观察者模式/ java实现附代码 /

    /注:场景和例子引用github上的设计模式.传送门:https://github.com/iluwatar/java-design-patterns/tree/master/observer 场景: ...

  4. e679. 浮雕化图像

    This example demonstrates a 3x3 kernel that embosses an image. Kernel kernel = new Kernel(3, 3, new ...

  5. linux -- Ubuntuserver图形界面下安装、配置lampp、phpmyadmin

    PHP开发和服务器运行环境首选LAMP组合,即Linux+Apache+Mysql+Php/Perl/Python,能最优化服务器性能.如何在本地电脑Ubuntu 中安装和配置LAMP环境搭建?Ubu ...

  6. Openstack镜像和密码

    #!/bin/sh passwd ubuntu<<EOF ubuntu ubuntu EOF sed -i 's/PasswordAuthentication no/PasswordAut ...

  7. 【Java集合的详细研究7】Set和List 的关系与区别

    两个接口都是继承自Collection. List (inteface) 次序是List 的最重要特点,它确保维护元素特定的顺序. --ArrayList 允许对元素快速随机访问. --LinkedL ...

  8. .net FrameWork各个版本之间的发展[转]

    上个星期看到了.NET 4.0框架退休日期逐渐临近文章,发现自己一直在使用NET  FrameWork,身为一个NET程序员,里面大概的区别自己还是知道的,但是自己要说出个所以然来了,发现还是有点力不 ...

  9. Buff系统框架设计

    Buff的配置文件 BufType: 1: 精神类Buf 2: 物理类Buf 3.元素类Buf 4.其他类Buf 5.被动类BufBufSubType: 1000-1999 精神子类 2000-299 ...

  10. NLP入门相关——学习笔记

    近义词.一词多义 GPT.ELMO.Bert