Java与泛型

原创 2005年06月01日 15:09:00

泛型最早由C++引入,是对类型的抽象。泛型也许是自从jdk1.1以来最大的变化。

为什么使用泛型

泛型可以带来两个有点,第一,类型的安全,不必再为CalssCastException而苦恼,同时不再为不停的显示转换而烦躁。第二,更快速,高效的代码(对JDK1.5来说未必如此)。

泛型类

泛型类实现了对类型的抽象,可以使用类型来做参数,在使用的时候再使用具体的类型参数。例如我们可以定义一个Pair类,该Pair类用来记录两个相同类型的变量,但是随着类型的变化可能有很多种不同的Pair类型或者有一个类型为ObjectPair类。下面我们就不同的情况来详细说明:

/**

 * jdk 1.4 object pair

*/

public class Pair {

   

    private Object first;

    private Object second;

   

    public Pair(Object first, Object second) {

        this.first = first;

        this.second = second;

    }

   

    public Object getFirst(){

        return first;

    }

   

    public Object getSecond(){

        return second;

    }

}

当然也可以使用具体的类型(例如String)来生成具体的Pair类型。

/**

 * jdk 1.5 Generic Pair

 */

public class Pair<T>{

    private T first;

    private T second;

   

    public Pair(T first, T second) {

        this.first = first;

        this.second = second;

    }

   

    public T getFirst() {

        return first;

    }

   

    public T getSecond() {

        return second;

    }

}

可以按以下方法使用:

public static void main() {

        Pair<String> strPair = new Pair<String>("first", "second");

        String first = strPair.getFirst();

        String second = strPair.getSecond();   

    }

相对于Object Pair来说,从Pair中得到的对象已经是定义的类型(String),而不需要在做Cast

泛型方法

可以在泛型类内部定义泛型方法,也可以在普通类内部定义泛型方法。以下讲述在普通类内部定义泛型方法的过程。

例如我们要从一个数组中取得中间值,然后作为返回值。

/**

 * jdk 1.5 Generic Method

 */

public class ArrayAlg {

   

    /** Creates a new instance of ArrayAlg */

    public ArrayAlg() {

    }

   

    public static <T> T getMiddle(T[] a) {

        return a[a.length / 2];

    }

   

    public static void main(String[] args) {

        String[] strArr = new String[] {"first", "second", "third" };

        string strMidVal = ArrayAlg.<String>getMiddle(strArr);

    }

}

上述泛型方法的使用可以得到简化,通过getMiddle()的参数我们可以得到类型参数所以上述调用可简化为:

String strMidVal = ArrayAlg.getMiddle(strArr);

限制类型参数

有些时候我们需要对类型参数加上一定的限制,例如如果我们希望我们的Pair类型用来存放Number的子类型,那么我们可以使用如下的方式来做限制:

/**

 * jdk 1.5 Generic Pair

 */

public class Pair<T extends Number>{

    private T first;

    private T second;

   

    public Pair(T first, T second) {

        this.first = first;

        this.second = second;

    }

   

    public T getFirst() {

        return first;

    }

   

    public T getSecond() {

        return second;

    }

   

    public static void main() {

        Pair<Integer> strPair = new Pair<Integer>(new Integer(0), new Integer(1));

        Integer first = strPair.getFirst();

        Integer second = strPair.getSecond();

       

    }

}

这时如果你继续使用:

Pair<String> strPair = new Pair< String >(first, second);

则会报告编译器错误,因为String不是从Number集成而来的。

 

其实在<T extends Number>中的extends并不仅表示继承关系也可以表示实现关系,例如,使用如下的方式也可以对类型参数加以限制。

public class Pair<T extends Serializable>{

}

当需有两个以上的类型限制时,可以使用&来连接限制类型。例如:也可以使用如下的方式对类型参数加以限制:

public class Pair<T extends Serializable & Comparable>{

}

但是要求,又多个限制时,所有的限制类型中只能有一个为类,其他必须为参数,且类必须在限制的第一个出现。

泛型代码和JVM

JVM内部并没有泛型的类型,也就是说JVM没有内在的支持泛型代码,但是JDK1.5之前的JVM并不能运行这些JDK1.5编译之后的泛型代码。

每当你定义一个泛型类型时,系统自动为你生成了一个隐藏的原始类型(Raw Type)。这些隐藏的原始类型的名字与去掉类型列表的泛型类型名字相同,所有的的参数类型定义的变量都会被类型参数的第一个限制类型替换,没有类型参数时使用Ojbect替代,例如针对于上述的Pair<T>类型,系统将自动生成如下隐藏的原始类型:

 

针对于如下的代码:

String buddy = strPair.getFirst();

编译器自动的添加了cast,将strPair.getFirst()返回的Object转变为String

 

泛型代码的四个重点:

l         虚拟机不支持泛型,只支持基本类型和方法。

l         所有的类型参数都回被该类型参数的限制类型取代。

l         合成的桥方法用来提供多态。

l         为了保证类型的安全,Cast被添加到了合适的位置。

泛型的使用的限制

大部分的限制都来源于Java泛型的实现机制(类型擦除)。

 

基本类型问题:

基本类型不能用来实例化类型参数。

 

运行时问题:

由于JVM内部没有泛型的支持,所以如下代码有相同的结果:

if (a instanceof Pair<String>) //<String>被忽略

if (a instanceof Pair<T>)      //<String>被忽略

同时如下代码<String>也会被被忽略。

Pair<String> p = (Pair<String>) a

当使用instanceof或者cast时,编译器会产生编译警告。

同时还有如下的奇怪现象:

Pair<String> stringPair = . . .;

Pair<Employee> employeePair = . . .;

if (stringPair.getClass() == employeePair.getClass()) // 总是相同

 

异常问题:

无法Throw或者Catch到泛型的异常。甚至从Exception或者Throwable继承泛型类是非法的。

 

数组问题:

不允许使用泛型数组。如下的代码会场生编译错误:

Pair<String>[] table = new Pair<String>(10);

 

泛型实例化的问题:

泛型不能被实例化,如下代码将会产生编译错误:

public Pair() {

        first = new T();

        second = new T();

}

但是可以使用Class.newInstance或者Array.newInstance来生成泛型实例。

 

静态上下文:

静态变量不能使用泛型。

静态方法中不能应用类的泛型属性变量。

泛型与继承

关于java泛型的讲解

  • 2011年06月15日 09:30
  • 63KB
  • 下载

一个java泛型的例子

public class Chongzai { //以下是方法重载的写法 // public static void printArray(Integer[] inputArray) { ...
  • forgot2015
  • forgot2015
  • 2017-02-20 19:37:45
  • 817

java泛型机制

  • 2011年11月06日 11:02
  • 35KB
  • 下载

[Java] 浅谈泛型的意义与不足

示意 - 泛型代码 泛型是什么 有什么用 为什么要有泛型示意 - 泛型代码// 类的泛型 class Two { public A first; public B second; ...
  • tjgykhulj
  • tjgykhulj
  • 2016-04-13 11:14:00
  • 2907

java增强之泛型练习

二 代码演示 /** * 泛型练习,包括使用类库中的泛型方法和自定义泛型方法 * @author yajun */ public class GenericTest extends TestCa...
  • momowuwenderen
  • momowuwenderen
  • 2013-12-30 23:28:05
  • 1670

JAVA泛型笔试面试总结

用途 数据结构类型的参数化 产生根源 例如在使用集合框架的时候,Vector里可以添加各种类型, v.add(new Person("tom",20)) Person p = (Person)v.el...
  • magic_wz
  • magic_wz
  • 2013-11-27 16:00:08
  • 1992

java泛型的一些常见用法

java泛型常见写法
  • aitangyong
  • aitangyong
  • 2017-02-09 08:57:30
  • 957

[进阶][重构实用] 泛型 限制 多个 多重控制

泛型 限制 多个 多重控制. java 重构有时候需要使用多个接口特性. 利用泛型的多种限制实现. class C<T extends Comparable<? super T> & Seria...
  • fei33423
  • fei33423
  • 2015-05-27 20:49:11
  • 1021

Java泛型详解,通俗易懂的写法

我们知道,使用变量之前要定义,定义一个变量时必须要指明它的数据类型,什么样的数据类型赋给什么样的值。 假如我们现在要定义一个类来表示坐标,要求坐标的数据类型可以是整数、小数和字符串,例如: ...
  • daguairen
  • daguairen
  • 2016-10-27 09:19:02
  • 1643
收藏助手
不良信息举报
您举报文章:Java与泛型
举报原因:
原因补充:

(最多只允许输入30个字)