`
339751607
  • 浏览: 12760 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Generics-泛型

阅读更多

Generics简介

Generics是程序设计语言的一种技术,指将程序中数据类型进行参数化,它本质上是对程序的数据类型进行一次抽象,扩展语言的表达能力,同时支持更大粒度的代码复用。对于一些数据类型参数化的类和方法来说,它们往往具有更好的可读性、可复用性和可靠性。在设计集合类和它们的抽象操作时,往往需要将它们定义为与具体数据类型无关,在这种情况下,使用Generics就是非常适合的。举例来说,如果我们需要设计一个Stack类,有时需要元素为int类型的Stack,有时可能需要元素为Boolean或者Object类型的Stack。如果不使用Generics,我们通常需要定义不同的多个类,或者通过继承来实现。通过继承实现往往引起数据类型的转换问题,本文稍后对其进行分析。如果使用Generics技术,将Stack的元素类型进行参数化,那么Stack类的只需要实现一个版本,当需要某元素类型的Stack时,可以将类型作为参数来创建Stack对象。

Generics与Java语言的发展

Generics技术很早就被提出来了,但是Generics的实现和推广并非一帆风顺。这是因为Generics对语言规范的修改较大,对于编译技术要求也较高,另外,90年代,所有语言都在积极扩展面向对象技术,并没有太多时间关注Generics。到目前为止,只少量语言支持Generics,例如Ada, C++, Eiffel等。在C++中, Template被用来现实Generics,同时C++还提供了标准模板库(STL,Standard Template Library)供开发人员使用,极大提高了编程的效率,因此Generics常常又被混称作Template。

但是,到目前JDK1.4.1为止,Java语言不支持Generics。目前,对于数据类型的参数化,Java不能够直接实现,而是通常采用继承方式间接实现。由于Java中,所有类都是Object的子类,因此如果需要支持多种类型时,我们可以将类型定义为Object,在使用的时候,可以通过父类和子类进行相互转化来实现。以上面的Stack为例,我们可以将Stack的类型定义为Object类型,那么所有的Java对象都可以放在这个Stack中,在存取对象时候,我们可以将对象强行转化成所需要的类型。但是,这种方式也会带来一些问题。

  • 编译时,类型检查较为宽松,很多问题只有运行时候才能发现
  • 很难实现复杂的类型参数化,缺少表达能力
  • 增加使用者的麻烦,使用者需要对数据类型进行手工转换,并且进行类型检查
  • 由于Java区分Object和基本类型,因此这种方式不容易支持基本类型。






虽然当前Java语言规范并不支持Generics,但是一些组织和个人通过扩展Java语言的方式来实现Generics,其中比较著名的有GJ,PolyJ和NextGen。Java语言标准的制定组织Java Community Process(JCP)也早已收到关于在Java语言中支持Generics的建议,并且一直在讨论是否在Java语言支持Generics。其中,一个比较重要的里程碑是Gilad Bracha博士等在2001年提出的提议。

Generics的实现对于Java语言本身、Class文件格式和JVM构造都有较大的影响;另外,Generics在C++中的实现也存在一些问题,这些问题都应该在Java中尽量避免,最后,还有一部分人认为Generics的引入会损失Java语言的简洁性。基于这些考虑,目前Java语言仍然不能够支持Generics,但是根据一些相关人员的预计,2003年年底推出JDK1.5很可能要支持Generics。

 

Java中Generics的使用

在这一章中,我们将预览一些Java的Generics的用法,虽然这些技术并没有正式推出,但是根据Java Generics提议的草稿版本来看,这些用法都比较稳定且成熟,预计它们与正式规范不会有太大差别。另外,这些Generics程序通过专用的编译器,可以转换成兼容的Java类文件,并且可以在以前的JRE环境下运行,保证了向前兼容。本章节的部分内容来自Java语言Generics提议的草稿版本,另外一些例子也参考了GJ的运行结果。

1) Generics的定义:

为了使用Generics,首先必须定义支持Generics的类,接口或者方法,它与C++语言的模板的语法类似。<>用于包含参数化类型,参数化类型用Java标识符标识表示,通常使用大写字母,例如T,A,B等。

1.1) 类和接口定义
以下是最简单的Generics类定义,定义了一个参数化类型T1:
interface MyList<T1> {….. }

以下是支持多个参数化类型的接口:
interface MyList<T1,T2,T3> {….. }

Java支持带有限制的参数化类型,这意味着在构造该类对象的时候,实际类型必须满足限制条件。在下面的例子中,T1的类型必须实现类Comparable接口,T2类型必须为Component类的子类,否则将构造失败。这些限制检查工作通常在编译的时候就可以进行。
interface MyList<T1 implements Comparable, T2 extends Component> {}

复杂的定义可以带有限定的声明,甚至可以使用向前引用。
class Test<A implements ConvertibleTo<B>, B implements ConvertibleTo<A>{}

对于class的定义,基本与interface相同,此处不再详述。

1.2) 方法的定义
在方法中,通过定义参数化类型,可以提高方法的抽象级别,提高其可复用性。方法的参数化类型列表放在方法修饰符的后面,返回值的前面。在方法的参数中和方法体中,就可以直接使用参数化类型了。以下就是一个方法的例子。

 

public static  <Elem> void swap(Elem[] a,int i,int j)
   Elem temp=a[i];
   a[i]=a[j]
   a[j]=temp;
}

带限定的Generics方法定义例子。

 

public static  <Elem implements Comparable> void swap(Elem[] a,int I,int j)
   Elem temp=a[i];
   a[i]=a[j]
   a[j]=temp;
}

1.3) 实际例子
一但Java语言支持了Generics,Collection中的大部分类将用Generics方式重写,事实上,在一些支持Generics的Java扩展中,这些类已经被重写了。现在我们给出一个GJ中,使用Generics重写的Hashtable的定义。

 

public class Hashtable<K,V> 
extends Dictionary<K,V> 
implements Map<K,V>, java.lang.Cloneable, java.io.Serializable {  
public V put(K key, V value) {……}
…….
}

与以前的Hashtable定义不同的是,它增加了两个用于表达Key和Value参数化类型(K,V),由于Dictionary和Map都将支持Generics,因此它们都使用Generics的表达方式。

2) Generics的使用:

2.1) 创建对象
Generics类在使用之前,必须按照定义进行初始化,设置参数化类型的实际类型,以下是构造Hashtable和Vector的一些例子。

 

Hashtable<Integer,String> ht1=new Hashtable<Integer,String>();
Hashtable<Integer,String> ht2=null;
Vector<String> v1=new Vector<String>();
Vector<Integer> v2=new Vector<Integer>();

2.2) Generics对象的类
在上例中,对于v1和v2对象来说,它们都是Vector类的对象,它们有相同的Class类型,换句话说,在运行时,以下表达式为真。虽然它们使用不同的参数类型创建,但是它们的Class类型是相同的。 Assert(V1.getClass().equals(v2.getClass()));

但是,如果我们定义一个 带有Vector<String>参数的方法,在调用该方法时,传入一个Vector<Integer>的对象,这将会导致编译失败。这是因为在编译时,这两个对象被当作不同的类型。

 

void  method(Vector<String> v) {};
…
Vector<Integer> v2=new Vector<Integer>();
method(v2);//编译失败

2.3) 类型检查
在构造一个Generics对象时,编译器将首先检查参数化类型是否有效,例如是否满足限制条件等,确定所有参数化类型,然后编译器将在使用这些参数化类型的地方进行类型检查,如果符合定义,那么编译通过,否则将编译失败,报告类型检查错误。因此,通过这种方式,编译器可以检查出很多类型不匹配的错误,避免开发人员的错误,这也是Generics的重要优点之一。以下是类型检查的一些例子:

 

Vector<String> strvector=new Vector<String>();
    Vector<Integer> intvector=new Vector<Integer>();
    strvector.add(new String());//OK
    strvector.add(new Object());//编译失败,需要String类
strvector.add(new Integer());//编译失败,需要String类
intvector.add(new Integer());//OK
intvector.add(new String());//编译失败,需要Integer类

2.4) 类的强制转换
对于Generics类的强制转化的原理,我们可以使用通用的转化规则进行操作。需要注意的一点是,同一个Generics类所定义的不同参数化类型的对象之间是不能进行转化的。例如Vector<Object>和Vector<String>之间就是不能转化的。另外,Object也不能够转化成Generics类型,但是Generics类可以转化成Object。

以下是一些转化的例子

 

Class Dictionary<A,B> extends Object{}
Class Hashtable<A,B> extends Dictionary<A,B> {}
Dictionary<String,Integer> d=new Dictionary<String,Integer>();
Hashtable<String,Integer> h=new Hashtable <String,Integer>();
Hashtable<Float,Double> hfd=new Hashtable<Float,Double>();
Object o=new Object();
1) d= (Dictionary<String,Integer>)h//编译成功,运行成功;它们具有父子类关系。
2) h=(Hashtable<String,Integer>)d;// 编译成功,运行失败;它们具有父子类关系。
3) h=(Hashtable<String,Integer>)o;//编译失败,Object不能转化成Generics类;
4) hfd=(Hashtable<Float,Double>)d;//编译失败;

Generics的设计和实现

1) Java的Generics与C++的Template

由于Java的Generics设计在C++的Template之后,因此Java的Generics设计吸取Template的很多经验和教训,特别是Generics避免了一些Template已知的一些问题。首先,与Template不同的是,Generics的声明是需要进行类型检查的,而Template不提供这一功能,这使得Generics的使用更加安全。另外,Java的Generics程序只需要编译一次,以后所有程序就可以复用这个类字节码,而Template的实现是为每一个使用Template变量编译成一个新类,这会引起一定的冗余代码。

2) Generics的实现方案

Generics Java目前有很多不同的实现,比较著名的有GJ,PolyJ和NextGen。其中,GJ(Generic Java)是Gilad Bracha博士等设计和开发的支持Generics的Java编译器,它是较早,且较全面的Generics的解决方案,实际上,GJ是目前Java语言的一个扩展,主要对编译器进行了扩展,以支持带有Generics 的Java 程序。

GJ的工作原理本质上就是消除程序中的Generics语法,并将其转化成等价的无Generics的程序,这样编译的结果就是传统的类字节码,它们可以在传统的JVM中的运行,保证了向前兼容。编译的过程就是将所有参数化类型的变量都替换为Object,通过这种方式消除所有的参数化类型。由于Java中,所有对象都可以转换程Object的对象,因此通过这种方式可以消除参数化类型,但这种方法也有一个问题,那就是无法处理基本类型,因为Object与基本类型无法相互转换。在消除了参数化类型后,在适当的地方还需要加上一些类型检查和转化语句。

例如Stack类的Generics源程序可能如下表示:

 

public class Stack<A> {
public void push(A elem){…. }
public A pop(){…..}
}

经过GJ改写后,程序将变为如下:

 

public class Stack {
public void push(Object elem){….}
public Object pop(){….}
}

在使用Generics类的时候,我们首先设置参数化类型的值,在下例子我们传入Button类型。

 

Stack<Button> s = new Stack<Button>();
Button b = new Button("OK");
s.push(b);
b=s.pop();

GJ在编译以上代码时,在消除<>后,首先需要检查b是否能够转换成Button,如果b不能够转换成Button,编译器将报类型转化错误。同时, 在pop方法的返回值处,我们需要将Object类型显式转换为Button类型。以下就是GJ转化的结果;

 

Stack  s = new Stack();
Button b = new Button("b");
s.push(b);
b=(Button)s.pop();

以上只描述了一些基本原理,在真正的实现中,GJ还需要处理继承,约束和类型转换等复杂问题。从实现的效果来看,GJ是非常成功地支持了Generics,并且提供了通过Generics改写的java.utils.collection包。更多的实现技术,请参考相关资料。


结论

Generics的出现将改变一些Java程序员的编程风格,以往所有不确定类型的对象都被定义为Object,需要使用对象时,通过强制类型转化获取,而Generics的出现将可用于管理一些类型抽象的类,让编译器来检查更多的类型匹配的问题,以减轻程序员的负担。但同时,Generics的引入,增加了Java程序的抽象程度,增加程序理解的难度。

<!-- CMA ID: 162771 --><!-- Site ID: 10 --><!-- XSLT stylesheet used to transform this file: dw-article-6.0-beta.xsl -->

参考资料

关于作者

欧阳辰,2001年毕业于北京大学计算机系,获硕士学位,SCJP(Sun Certificated Java Programmer),现任某公司软件工程师,长期从事java软件的研究和开发工作,已发表多篇Java相关的技术文章。联系方式 yeekee@sina.com

 

原文地址: http://www.ibm.com/developerworks/cn/java/Java_and_Generics/index.html

分享到:
评论

相关推荐

    黑马程序员----泛型与反射的小运用

    首先,我们来看泛型(Generics)。泛型是在Java SE 5.0引入的新特性,它允许在编译时检查类型安全,并且所有的强制类型转换都是自动和隐式的,提高了代码的重用率。泛型的基本语法是在类、接口或方法声明前加上尖...

    generics-tutorial.pdf

    自JDK 1.5以来,Java编程语言引入了一系列重要的扩展功能,其中最重要的一个特性就是泛型(Generics)。本文档旨在向读者介绍Java中的泛型,并通过与类似语言如C++中的模板进行对比,帮助读者理解其相似性和差异性。...

    Generics-four-arithmetic.zip_float_site:www.pudn.com

    在本例中,“Generics-four-arithmetic.zip_float_site:www.pudn.com”是一个关于使用泛型进行四则运算(加减乘除)的示例,特别关注了处理`int`、`double`和`float`类型的数值。我们将深入探讨这个主题,并结合...

    实例188 -泛型化接口与最大值

    在Java编程语言中,泛型(Generics)是自Java 5版本引入的一项重要特性,它允许我们在类、接口和方法中使用类型参数,从而提高了代码的可复用性和安全性。"实例188 - 泛型化接口与最大值"这个主题探讨了如何在接口...

    6-generics(泛型6).pdf

    泛型是TypeScript中的一个重要特性,它允许我们在编写代码时定义通用的类型,从而提高代码的可重用性和类型安全性。在大型系统开发中,泛型的使用是至关重要的,因为它们确保组件不仅可以处理当前的数据类型,还能...

    Java-Generics-and-Collections-Example:Java泛型和集合的示例

    在"Java-Generics-and-Collections-Example-master"这个压缩包中,可能包含了各种关于Java泛型和集合的实例代码,如创建和操作泛型集合、遍历和搜索元素、使用泛型方法等。通过研究这些示例,开发者可以深入理解这两...

    solder-generics-impl-0.5.1.zip

    【标题】"solder-generics-impl-0.5.1.zip" 是...了解这些知识点后,无论是想要使用"RestClient"进行REST服务的调用,还是研究"solder-generics-impl"的泛型实现,都有助于提升你的Java开发技能和对RESTful架构的理解。

    java泛型源码-Java-Generics-Our-Generics-Class-Part-3-Source-code:通用课程

    在本教程中,我们将深入探讨Java泛型的源码实现,特别是"Java-Generics-Our-Generics-Class-Part-3-Source-code"项目中的部分。 首先,泛型的主要目标是消除运行时类型转换异常,例如`ClassCastException`。通过在...

    Java-Generics-and-Collections

    根据提供的文件信息,我们可以深入探讨《Java泛型与集合》这本书中的关键知识点。该书由Maurice Naftalin和Philip Wadler合著,并于2007年由O'Reilly Media出版。以下是对该书内容的一个综合概述,旨在帮助读者理解...

    java泛型源码-Java-Generics-Our-Generics-Class-Part-3-Source-code:JavaGeneri

    在“Java Generics - Our Generics Class - Part 3 Source code”中,我们可以期待深入理解如何设计和实现自己的泛型类。这部分源代码可能包含对泛型类、泛型方法、通配符、边界以及类型擦除等概念的实例。以下是...

    aidanhs-tmp-parse-generics-shim-提议的RFC#1583的稳定填充程序; 提供用于解析泛型和where子句的宏-Rust开发

    parse-generics此存储库包含与建议的RFC#1583相关的几段内容:parse-generics-poc-parse-generics的概念验证实现此存储库包含与建议的RFC#1583相关的几段内容:parse-generics -poc-RFC的概念验证实现。...

    java教学基础系列一

    ### Week 11: Generics * Generic Classes and Type Parameters + 泛型类:ArrayList,使用类型参数指定元素类型 + Type parameter:E,用于指定元素类型 * Generic programming:创建可以与多种类型一起使用的...

    泛型java的泛型知识,非常有用

    - 类如 `class Java_Generics,V&gt;`,`K` 和 `V` 就是类型参数,类似于函数中的变量,它们在实例化时会被具体的类型替换。 - 示例中,`TestGen0, String&gt;` 就是泛型类的具体实例,其中 `K` 被替换为 `String`,`V` ...

    generics-sop:使用产品的真实总和的泛型编程

    在Haskell这个函数式编程语言中,泛型编程是一种强大的技术,它允许程序员编写与数据类型无关的代码。"generics-sop"库是Haskell社区中广泛使用的一个工具,它提供了一种处理通用数据类型的方法,特别是通过利用...

    go-generics-example

    《Go泛型实战指南——基于"go-generics-example"》 在Go语言的世界里,泛型是一种强大的工具,它允许我们编写可复用的代码,适用于多种数据类型,而无需重复编写相同逻辑。"go-generics-example"项目正是一个深入...

    java泛型,java知识

    - 泛型(Generics)允许在定义类、接口和方法时声明类型参数,从而创建泛型类、泛型接口和泛型方法。这使得代码可以处理多种类型的对象,而不必为每种类型都创建单独的类或方法。 - 类型参数通常以大写字母表示,...

    ull-esit-inf-dsi-20-21-prct06-generics-solid-alu0101202952:GitHub Classroom创建的ull-esit-inf-dsi-20-21-prct06-generics-solid-alu0101202952

    尽管提供的描述没有具体信息,我们可以根据标题中的关键词“generics”(泛型)和“solid”来推测这与编程语言Java中的泛型特性和面向对象设计原则的SOLID有关。同时,标签“CSS”表明可能也涉及到了网页样式和布局...

    swift代码-泛型试验案例

    在本实验案例中,我们将深入探讨Swift中的泛型(Generics),这是一种允许我们编写可重用代码的强大工具,可以在多种类型上工作,而无需多次重复实现相同逻辑。泛型是Swift语言的一个核心特性,它增强了代码的灵活性...

    Java-Generics-and-Collections-2:Java Generics and Collections Java泛型和集合

    泛型与集合 使用 进行初步翻译. 将利用碎片时间进行整理和校对,完整的时间段适合做其他需要大量思考的事,如果你有兴趣欢迎提交PR。 TODO 数据校对 目录 2.4 获取和放置原则 2.5 数组 2.6 通配符与类型参数 2.7 ...

Global site tag (gtag.js) - Google Analytics