当前位置:文档之家› 第10-14章 泛型

第10-14章 泛型

第10章泛型

泛型是JDK1.5中新加入的元素,它改变了核心API中的许多类和方法。使用泛型,可以建立以类型安全模式处理各种数据的类、接口和方法。许多算法不论运用哪一种数据类型,它们在逻辑上是一样的。使用泛型,一旦定义了一个算法,就独立于任何特定的数据类型,而且不需要额外的操作,就可以将这个算法应用到各种数据类型中。正由于泛型的强大功能,从根本上改变了Java代码的编写方式。

本章将介绍泛型的语法和应用,同时展示泛型如何提供类型安全。

10.1 泛型的本质

泛型在本质上是指类型参数化。所谓类型参数化,是指用来声明数据的类型本身,也是可以改变的,它由实际参数来决定。在一般情况下,实际参数决定了形式参数的值。而类型参数化,则是实际参数的类型决定了形式参数的类型。

举个简单的例子。方法max()要求返回两个参数中较大的那个,可以写成:

Integer max(Integer a, Integer b){

return a>b?a:b;

}

这样编写代码当然没有问题。不过,如果需要比较的不是Integer类型,而是Double 或是Float类型,那么就需要另外再写max()方法。参数有多少种类型,就要写多少个max()方法。但是无论怎么改变参数的类型,实际上max()方法体内部的代码并不需要改变。如果有一种机制,能够在编写max()方法时,不必确定参数a和b的数据类型,而等到调用的时候再来确定这两个参数的数据类型,那么只需要编写一个max()就可以了,这将大大降低程序员编程的工作量。

在C++中,提供了函数模板和类模板来实现这一功能。而从JDK1.5开始,也提供了类似的机制:泛型。从形式上看,泛型和C++的模板很相似,但它们是采用完全不同的技术来实现的。

在泛型出现之前,Java的程序员可以采用一种变通的办法:将参数的类型均声明为Object类型。由于Object类是所有类的父类,所以它可以指向任何类对象,但这样做不能保证类型安全。

泛型则弥补了上述做法所缺乏的类型安全,也简化了过程,不必显示地在Object与实际操作的数据类型之间进行强制转换。通过泛型,所有的强制类型转换都是自动和隐式的。

第10章泛型

因此,泛型扩展了重复使用代码的能力,而且既安全又简单。

10.2 一个关于泛型的简单例子

这里用一个简单的例子来开始泛型的学习,让读者对泛型有一个感性的认识。

【例10.1】泛型类示例。

//--------------文件名Generic.java,程序编号10.1-----------

//声明一个泛型类

public class Generic{

T ob; //ob的类型是T,现在不能具体确定它的类型,需要到创建对象时才能确定

Generic(T o){

ob = o;

}

//这个方法的返回类型也是T

T getOb(){

return ob;

}

//显示T的类型

void showType(){

System.out.println("Type of T is:"+ob.getClass().getName() );

}

}

下面这个程序使用上面这个泛型类。

//--------------文件名demoGeneric.java,程序编号10.2-----------

public class demoGeneric{

public static void main(String args[]){

//声明一个Integer类型的Generic变量

Generic iobj;

//创建一个Integer类型的Generic对象

iobj = new Generic(100);

//输出它的一些信息

iobj.showType();

int k = iobj.getOb();

System.out.println("k="+k);

//声明一个String类型的Generic变量

Generic sobj;

//创建一个Double类型的Generic对象

sobj = new Generic("Hello");

//输出它的一些信息

sobj.showType();

String s = sobj.getOb();

System.out.println("s="+s);

}

}

·305·

Java开发技术大全

程序的输出结果如下:

Type of T is:https://www.doczj.com/doc/606229371.html,ng.Integer

k=100

Type of T is:https://www.doczj.com/doc/606229371.html,ng.String

s=Hello

下面来仔细分析一下这个程序。

首先,注意程序是如何声明Generic的:

public class Generic

其中,T是类型参数的名称。在创建一个对象时,这个名称用作传递给Generic的实际类型的占位符。因此,在Generic中,每当需要类型参数时,就会用到T。注意,T是被括在“<>”中的。每个被声明的类型参数,都要放在尖括号中。由于Generic使用了类型参数,所以它是一个泛型类,也被称为参数化类型。

然后,T来声明了一个成员变量ob:

T ob;

由于T只是一个占位符,所以ob的实际类型要由创建对象时的参数传递进来。比如,传递给T的类型是String,那么ob就是String类型。

最后,看一下Generic的构造方法:

Generic(T o){

ob = o;

}

它的参数o的类型也是T。这意味着o的实际类型,是由创建Generic对象时传递给T 的类型来决定的。而且,由于参数o和成员变量ob都是T类型,所以无论实际类型是什么,二者都是同一个实际类型。

参数T还可以用来指定方法的返回类型,如下所示:

T getOb(){

return ob;

}

因为ob是T类型,所以方法的返回类型必须也由T来指定。

showType()方法通过使用Class对象来获取T的实际类型,这就是第9章介绍的RTTI 机制。

综合上面的用法,可以看出,T是一个数据类型的说明,它可以用来说明任何实例方法中的局部变量、类的成员变量、方法的形式参数以及方法的返回值。

注意:类型参数T不能使用在静态方法中。

程序9.2示范了如何使用一个泛型类Generic。它首先声明了Generic的一个整型版本:Generic iobj;

·306·

第10章泛型

其中,类型Integer被括在尖括号内,表明它是一个类型实际参数。在这个整型版本中,所有对T的引用都会被替换为Integer。所以ob和o都是Integer类型,而且方法getOb()的返回类型也是Integer类型的。

注意Java的编译器并不会创建多个不同版本的Generic类。相反,编译器会删除所有的泛型信息,并进行必要的强制类型转换,这个过程被称为擦拭或擦除。但对程序员而言,这一过程是透明的,仿佛编译器创建了一个Generic的特定版本。这也是Java的泛型和C++的模板类在实现上的本质区别。

下面这条语句真正创建了一个Integer版本的实例对象:

iobj = new Generic(100);

其中,100是普通参数,Integer是类型参数,它不能被省略。因为iobj的类型是Generic,所以用new返回的引用必须是Generic类型。无论是省略Integer,还是将其改成其他类型,都会导致编译错误。例如:

iobj = new Generic(1.234); //错误

因为iobj是Generic类型,它不能引用Generic对象。泛型的一个好处就是类型检查,所以它能确保类型安全。

再回顾一下Generic的构造方法的声明:

Generic(T o)

其中,实际参数应该是Integer类型,而现在的实际参数100是int型,这似乎不正确。实际上,这里用到了Java的自动装箱机制(将在12.3节中介绍)。当然,创建对象也可以写成这种形式:

iobj = new Generic(new Integer(100));

但这样写没有任何必要。

然后,程序通过下面的语句获得ob的值:

int k = iobj.getOb();

注意,getOb的返回类型也是Integer的。当它赋值给一个int变量时,系统会自动拆箱,所以没有必要这么来写:

int k = iobj.getOb().intValue();

后面创建String版本的过程和前面的完全一样,在此不再赘述。

最后还有一点需要读者特别注意:声明一个泛型实例时,传递给形参的实参必须是类类型,而不能使用int或char之类的简单类型。比如不能这样写:

Generic ob = new Generic (100); //错误

如果要使用简单类型,只能使用它们的包装类,这也是泛型和C++模板的一个重要区别。

·307·

Java开发技术大全

·308·10.3 带两个类型参数的泛型类

在泛型中,可以声明一个以上的类型参数,只需要在这些类型参数之间用逗号隔开。下面看一个简单的例子。

【例10.2】带两个类型参数的泛型。

//--------------文件名twoGen.java,程序编号10.3-----------

//本类带有两个类型参数

public class twoGen{

T ob1;

V ob2;

//构造方法也可以使用这两个类型参数

twoGen(T o1, V o2){

ob1 = o1;

ob2 = o2;

}

//显示T和V的类型

void showTypes(){

System.out.println("Type of T is "+ob1.getClass().getName());

System.out.println("Type of V is "+ob2.getClass().getName());

}

T getOb1(){

return ob1;

}

V getOb2(){

return ob2;

}

}

下面这个程序演示流如何使用上面这个泛型类。

//--------------文件名simpGen.java,程序编号10.4-----------

public class simpGen{

public static void main(String args[]){

twoGen tgObj; //指定类型参数的实际类型

//构造方法中需要再次指定类型参数,同时还要传递实际参数

tgObj = new twoGen(100,"Hello");

tgObj.showTypes();

int v = tgObj.getOb1();

System.out.println("value: "+v);

String str = tgObj.getOb2();

System.out.println("value: "+str);

}

}

程序的输出结果如下:

第10章泛型

Type of T is https://www.doczj.com/doc/606229371.html,ng.Integer

Type of V is https://www.doczj.com/doc/606229371.html,ng.String

value: 100

value: Hello

与只有一个类型参数的泛型相比,本例并没有什么难于理解的地方。Java并没有规定

这两个类型参数是否要相同,比如,下面这样来创建对象也是可以的:

twoGen tgObj = new twoGen ("Hello","World");

这样T和V都是String类型,这个例子并没有错。但如果所有的实例都是如此,就没

有必要用两个参数。

10.4 有界类型

在前面的例子中,参数类型可以替换成类的任意类型。在一般情况下,这是没有问题的,但有时程序员需要对传递给类型参数的类型加以限制。

比如,程序员需要创建一个泛型类,它包含了一个求数组平均值的方法。这个数组的

类型可以是整型、浮点型,但显然不能是字符串类型或是其他非数值类型。如果程序员写

出如下所示的泛型类。

//--------------文件名Stats.java,程序编号10.5-----------

class Stats{

T [] nums;

Stats (T [] obj){

nums = obj;

}

double average(){

double sum = 0.0;

for (int i=0; i

sum += nums[i].doubleValue(); //这里有错误!

return sum / nums.length;

}

}

其中,nums[i].doubleValue()是返回Ingeger、Double等数据封装类转换成双精度数后

的值,所有的Number类的子类都有这个方法。但问题是,编译器无法预先知道,程序员

的意图是只能使用Number类来创建Stats对象,因此,编译时会报告找不到doubleValue()

方法。

为了解决上述问题,Java提供了有界类型(bounded types)。在指定一个类型参数时,

可以指定一个上界,声明所有的实际类型都必须是这个超类的直接或间接子类。语法形式

如下:

class classname

·309·

Java开发技术大全

采用这种方法,可以正确地编写Stats类。

【例10.3】有界类型程序示例。

//--------------文件名Stats.java,程序编号10.6-----------

//下面这个泛型的实际类型参数只能是Number或它的子类

class Stats{

T [] nums;

Stats (T [] obj){

nums = obj;

}

double average(){

double sum = 0.0;

for (int i=0; i

sum += nums[i].doubleValue(); //现在正确!

return sum / nums.length;

}

}

程序10.7演示了如何使用这个类。

//--------------文件名demoBounds.java,程序编号10.7-----------

public class demoBounds{

public static void main(String args[]){

Integer inums[] = {1,2,3,4,5};

Stats iobj = new Stats(inums);

System.out.println("平均值为:"+iobj.average());

Double dnums[] = {1.1,2.2,3.3,4.4,5.5};

Stats dobj = new Stats(dnums);

System.out.println("平均值为:"+dobj.average());

//如果像下面这样创建String类型的对象将无法编译通过

//String snums[] = {"1","2","3","4","5"};

//Stats sobj = new Stats(snums);

//System.out.println("平均值为:"+sobj.average());

}

}

程序的输出结果如下:

平均值为:3.0

平均值为:3.3

程序10.6和程序10.7的上界都是类,实际上,接口也可以用来做上界。比如:class Stats

注意:这里使用的关键字仍然是extends而非implements。

一个类型参数可以有多个限界,比如:

class Stats

·310·

第10章泛型

注意:限界类型用“&”分隔,因为逗号用来分隔类型参数。在多个限界中,可以有多个接口,但最多只能有一个类。如果用一个类作为限界,它必须是限界列表中的

第一个。

10.5 通配符参数

前面介绍的泛型已经可以解决大多数的实际问题,但在某些特殊情况下,仍然会有一些问题无法轻松地解决。

以Stats类为例,假设在其中存在一个名为doSomething()的方法,这个方法有一个形式参数,也是Stats类型,如下所示:

class Stats{

……

void doSomething(Stats ob){

System.out.println(ob.getClass().getName());

}

}

如果在使用的时候,像下面这样写是有问题的:

Integer inums[] = {1,2,3,4,5};

Stats iobj = new Stats(inums);

Double dnums[] = {1.1,2.2,3.3,4.4,5.5};

Stats dobj = new Stats(dnums);

dobj.doSomething(iobj); //错误

注意看出现错误的这条语句:

dobj.doSomething(iobj);

dobj是Stats类型,iobj是Stats类型,由于实际类型不同,而声明时用的是:

void doSomething(Stats ob)

它的类型参数也是T,与声明对象时的类型参数T相同。于是在实际使用中,就要求iobj和dobj的类型必须相同。

读者也许会想,将doSomething的声明改一下:

void doSomething(Stats ob)

但这样是无法通过编译的,因为并不存在一个State的泛型类。解决这个问题的办法是使用Java提供的通配符“?”,它的使用形式如下:

genericClassName

比如,上面的doSomething可以声明成这个样子:

·311·

Java开发技术大全

void doSomething(Stats ob)

它表示这个参数ob可以是任意的Stats类型,于是调用该方法的对象就不必和实际参数对象类型一致了。下面这个例子实际演示了通配符的使用。

【例10.4】通配符使用示例。

//--------------文件名Stats.java,程序编号10.8-----------

class Stats{

T [] nums;

Stats (T [] obj){

nums = obj;

}

double average(){

double sum = 0.0;

for (int i=0; i

sum += nums[i].doubleValue();

return sum / nums.length;

}

void doSomething(Stats ob){ //这里使用了类型通配符

System.out.println(ob.getClass().getName());

}

}

然后如程序10.9所示来调用它:

//--------------文件名demoWildcard.java,程序编号10.9-----------

public class demoWildcard{

public static void main(String args[]){

Integer inums[] = {1,2,3,4,5};

Stats iobj = new Stats(inums);

Double dnums[] = {1.1,2.2,3.3,4.4,5.5};

Stats dobj = new Stats(dnums);

dobj.doSomething(iobj); //iobj和dobj的类型不相同

}

}

程序的输出结果如下:

Stats

读者应该注意到这个声明:

void doSomething(Stats ob) //这里使用了类型通配符

它与泛型类的声明有区别,泛型类的声明中,T是有上界的:

class Stats

其中,通配符“?”有一个默认的上界,就是Number。如果想改变这个上界,也是可以的,比如:

Stats ob

·312·

第10章泛型

但是不能写成这样:

Stats ob

因为Integer是Number的子类,而String不是Number的子类。通配符无法将上界改变得超出泛型类声明时的上界范围。

最后读者需要注意一点,通配符是用来声明一个泛型类的变量的,而不能创建一个泛型类。比如下面这种写法是错误的:

class Stats{……}

10.6 泛型方法

在C++中,除了可以创建模板类,还可以创建模板函数。在Java中也提供了类似的功能:泛型方法。一个方法如果被声明成泛型方法,那么它将拥有一个或多个类型参数,不过与泛型类不同,这些类型参数只能在它所修饰的泛型方法中使用。

创建一个泛型方法常用的形式如下:

[访问权限修饰符] [static] [final] <类型参数列表> 返回值类型方法名([形式参数列表])

访问权限修饰符(包括private、public、protected)、static和final都必须写在类型参数列表的前面。

返回值类型必须写在类型参数表的后面。

泛型方法可以写在一个泛型类中,也可以写在一个普通类中。由于在泛型类中的任何方法,本质上都是泛型方法,所以在实际使用中,很少会在泛型类中再用上

面的形式来定义泛型方法。

类型参数可以用在方法体中修饰局部变量,也可以用在方法的参数表中,修饰形式参数。

泛型方法可以是实例方法或是静态方法。类型参数可以使用在静态方法中,这是与泛型类的重要区别。

使用一个泛型方法通常有两种形式:

<对象名|类名>.<实际类型>方法名(实际参数表);

和:

[对象名|类名].方法名(实际参数表);

如果泛型方法是实例方法,要使用对象名作为前缀。如果是静态方法,则可以使用对象名或类名作为前缀。如果是在类的内部调用,且采用第二种形式,则前缀都可以省略。

注意到这两种调用方法的差别在于前面是否显示地指定了实际类型。是否要使用实际类型,需要根据泛型方法的声明形式以及调用时的实际情况(就是看编译器能否从实际参数表中获得足够的类型信息)来决定。下面来看一个例子。

·313·

Java开发技术大全

【例10.5】泛型方法使用示例。

//--------------文件名demoGenMethods.java,程序编号10.10-----------

public class demoGenMethods{

//定义泛型方法,有一个形式参数用类型参数T来定义

public static void showGenMsg(T ob, int n){

T localOb = ob; //局部变量也可以用类型参数T来定义

System.out.println(localOb.getClass().getName());

}

public static void showGenMsg(T ob){

System.out.println(ob.getClass().getName());

}

public static void main(String args[]){

String str = "parameter";

Integer k = new Integer(123);

//用两种不同的方法调用泛型方法

demoGenMethods.showGenMsg(k,1);

showGenMsg(str);

}

}

程序中定义的两个泛型方法都是静态方法,这在泛型类中是不允许的。而且这两个泛型方法相互重载(参数的个数不同)。在方法体中,类型参数T的使用和泛型类中的使用是相同的。

再来看看如何调用这两个泛型方法:

demoGenMethods.showGenMsg(k,1);

showGenMsg(str);

在第一种调用形式中,传入了一个实际类型:,它表明类型参数是Integer 类型。要注意它的写法,在这种情况下,不能省略作为前缀的类名,也就是不能写成这样:

showGenMsg(k,1);

由于传递了一个实际的类型参数Integer,所以编译器知道如何将方法内部的占位符T 替换掉。不过需要注意,实际参数k的类型必须也是Integer型,否则编译器会报错。

第二种调用形式明显要简洁一些:

showGenMsg(str);

由于实参str是String类型的,编译器已经有了足够多的信息知道类型参数T是String 类型。程序的输出结果如下:

https://www.doczj.com/doc/606229371.html,ng.Integer

https://www.doczj.com/doc/606229371.html,ng.String

由于两种形式都能完成任务,而第二种明显要比第一种方便,所以多数情况下会使用第二种方式。不过在某些情况下,实参无法提供足够的类型信息给编译器,那么就需要使用第一种形式。例如:

·314·

第10章泛型

public void doSomething(){

T ob;

……

}

调用它的时候,根本就没有实际参数,所以编译器无法知道T的实际类型,这种情况下,就必须使用第一种形式。

前面还提到,泛型方法也可以写在泛型类中,比如:

public class Generic{

public void showGenMsg(U ob){

System.out.println(ob.getClass().getName());

}

……

}

这样写当然没有错误,但多数程序员都会将这个泛型方法所需要的类型参数U写到类的头部,即让泛型类带两个参数:

public class Generic{

public void showGenMsg(U ob){

System.out.println(ob.getClass().getName());

}

……

}

这样写,类的结构更为清晰。只有一种情况下必须在泛型类中再将方法声明为泛型方法:方法本身是静态的,那就无法像上面那样更改了。

10.7 泛型接口

除了泛型类和泛型方法,还可以使用泛型接口。泛型接口的定义与泛型类非常相似,它的声明形式如下:

interface 接口名<类型参数表>

下面的例子创建了一个名为MinMax的接口,用来返回某个对象集的最小值或最大值。

【例10.6】泛型接口示例。

//--------------文件名MinMax.java,程序编号10.11-----------

interface MinMax>{

T min();

T max();

}

这个接口没有什么特别难懂的地方,类型参数T是有界类型,它必须是Comparable 的子类。注意到Comparable本身也是一个泛型类,它是由系统定义在类库中的,可以用来

·315·

Java开发技术大全

比较两个对象的大小。

接下来的事情是实现这个接口,这需要定义一个类来实现。笔者实现的版本如下:

//--------------文件名MyClass.java,程序编号10.12-----------

class MyClass> implements MinMax{

T [] vals;

MyClass(T [] ob){

vals = ob;

}

public T min(){

T val = vals[0];

for(int i=1; i

if (vals[i].compareTo(val) < 0)

val = vals[i];

return val;

}

public T max(){

T val = vals[0];

for(int i=1; i

if (vals[i].compareTo(val) > 0)

val = vals[i];

return val;

}

}

类的内部并不难懂,只要注意MyClass的声明部分:

class MyClass> implements MinMax

看上去有点奇怪,它的类型参数T必须和要实现的接口中的声明完全一样。反而是接口MinMax的类型参数T最初是写成有界形式的,现在已经不再需要重写一遍。如果重写成下面这个样子:

class MyClass> implements MinMax>

编译将无法通过。

通常,如果一个类实现了一个泛型接口,则此类也是泛型类。否则,它无法接受传递给接口的类型参数。比如,下面这种声明是错误的:

class MyClass implements MinMax

因为在类MyClass中需要使用类型参数T,而类的使用者无法把它的实际参数传递进来,所以编译器会报错。不过,如果实现的是泛型接口的特定类型,比如:

class MyClass implements MinMax

这样写是正确的,现在这个类不再是泛型类。编译器会在编译此类时,将类型参数T用Integer代替,而无需等到创建对象时再处理。

最后写一个程序来测试MyClass的工作情况。

·316·

第10章泛型

//--------------文件名demoGenIF.java,程序编号10.13-----------

public class demoGenIF{

public static void main(String args[]){

Integer inums[] = {56,47,23,45,85,12,55};

Character chs[] = {'x','w','z','y','b','o','p'};

MyClass iob = new MyClass(inums);

MyClass cob = new MyClass(chs);

System.out.println("Max value in inums: "+iob.max());

System.out.println("Min value in inums: "+iob.min());

System.out.println("Max value in chs: "+cob.max());

System.out.println("Min value in chs: "+cob.min());

}

}

在使用类MyClass创建对象的方式上,和前面使用普通的泛型类没有任何区别。程序的输出结果如下:

Max value in inums: 85

Min value in inums: 12

Max value in chs: z

Min value in chs: b

10.8 泛型类的继承

和普通类一样,泛型类也是可以继承的,任何一个泛型类都可以作为父类或子类。不过泛型类与非泛型类在继承时的主要区别在于:泛型类的子类必须将泛型父类所需要的类型参数,沿着继承链向上传递。这与构造方法参数必须沿着继承链向上传递的方式类似。

10.8.1 以泛型类为父类

当一个类的父类是泛型类时,这个子类必须要把类型参数传递给父类,所以这个子类也必定是泛型类。下面是一个简单的例子。

【例10.7】继承泛型类示例。

//--------------文件名superGen.java,程序编号10.14-----------

public class superGen { //定义一个泛型类

T ob;

public superGen(T ob){

this.ob = ob;

}

public superGen(){

ob = null;

}

·317·

Java开发技术大全

public T getOb(){

return ob;

}

}

接下来定义它的一个子类:

//--------------文件名derivedGen.java,程序编号10.15-----------

public class derivedGen extends superGen{

public derivedGen(T ob){

super(ob);

}

}

注意derivedGen是如何声明成superGen的子类的:

public class derivedGen extends superGen

这两个类型参数必须用相同的标识符T。这意味着传递给derivedGen的实际类型也会传递给superGen。例如,下面的定义:

derivedGen number = new derivedGen(100);

将Integer作为类型参数传递给derivedGen,再由它传递给superGen,因此,后者的成员ob也是Integer类型。

虽然derivedGen里面并没有使用类型参数T,但由于它要传递类型参数给父类,所以它不能定义成非泛型类。当然,derivedGen中也可以使用T,还可以增加自己需要的类型参数。下面这个程序展示了一个更为复杂的derivedGen类。

//--------------文件名derivedGen.java,程序编号10.16-----------

public class derivedGen extends superGen{

U dob;

public derivedGen(T ob1, U ob2){

super(ob1); //传递参数给父类

dob = ob2; //为自己的成员赋值

}

public U getDob(){

return dob;

}

}

使用泛型子类和使用其他的泛型类没有区别,使用者根本无需知道它是否继承了其他的类。下面是一个测试用的程序:

//--------------文件名demoHerit_1.java,程序编号10.17-----------

public class demoHerit_1{

public static void main(String args[]){

//创建子类的对象,它需要传递两个参数,Integer类型给父类,自己使用String类型·318·

第10章泛型

derivedGen oa=new derivedGen

(100,"Value is: ");

System.out.print(oa.getDob());

System.out.println(oa.getOb());

}

}

程序的输出结果如下:

Value is: 100

10.8.2 以非泛型类为父类

前面介绍的泛型类是以泛型类作为父类,一个泛型类也可以以非泛型类为父类。此时,不需要传递类型参数给父类,所有的类型参数都是为自己准备的。下面是一个简单的例子。

【例10.8】继承非泛型类示例。

//--------------文件名nonGen.java,程序编号10.18-----------

publi

}

接下来定义一个泛型类作为它的子类:

//--------------文件名derivedNonGen.java,程序编号10.19-----------

public class derivedNonGen extends nonGen{

T ob;

public derivedNonGen(T ob, int n){

super(n);

this.ob = ob;

}

public T getOb(){

return ob;

}

}

这个泛型类仍然传递了一个普通参数给它的父类,所以它的构造方法需要两个参数。

下面是用于测试的程序:

//--------------文件名demoHerit_2.java,程序编号10.20-----------

public class demoHerit_2{

public static void main(String args[]){

derivedNonGen oa =new derivedNonGen ("Value is: ",

100);

System.out.print(oa.getOb());

System.out.println(oa.getNum());

}

}

·319·

Java开发技术大全

程序的输出结果如下:

Value is: 100

10.8.3 运行时类型识别

和其他的非泛型类一样,泛型类也可以进行运行时类型识别的操作,既可以使用反射机制,也可以采用传统的方法。比如,instanceof操作符。

需要注意的是,由于在JVM中,泛型类的对象总是一个特定的类型,此时,它不再是泛型。所以,所有的类型查询都只会产生原始类型,无论是getClass()方法,还是instanceof 操作符。

例如,对象a是Generic类型(Generic是例10.1中定义的泛型类),那么

a instanceof Generic<?>

测试结果为真,下面的测试结果也为真:

a instanceof Generic

注意:尖括号中只能写通配符“?”,而不能写Integer之类确定的类型。实际上在测试时,“?”会被忽略。

同样道理,getClass()返回的也是原始类型。若b是Generic类型,下面的语句:

a.getClass() ==

b.getClass()

测试结果也为真。下面的程序演示了这些情况。

【例10.9】泛型类的类型识别示例1。

//--------------文件名demoRTTI_1.java,程序编号10.21-----------

public class demoRTTI_1{

public static void main(String args[]){

Generic iob = new Generic(100);

Generic sob = new Generic("Good");

if (iob instanceof Generic)

System.out.println("Generic object is instance of Generic");

if (iob instanceof Generic)

System.out.println("Generic object is instance of

Generic");

if (iob.getClass() == sob.getClass())

System.out.println("Generic class equals Generic

class");

}

}

程序的输出结果如下:

Generic object is instance of Generic

·320·

第10章泛型

Generic object is instance of Generic

Generic class equals Generic class

泛型类对象的类型识别还有另外一个隐含的问题,它会在继承中显示出来。例如,对象a是某泛型子类的对象,当用instanceof来测试它是否为父类对象时,测试结果也为真。

下面的例子使用了例10.7中的两个类:superGen和derivedGen。

【例10.10】泛型类的类型识别示例2。

//--------------文件名demoRTTI_2.java,程序编号10.22-----------

public class demoRTTI_2{

public static void main(String args[]){

superGen oa = new superGen(100);

derivedGen ob = new derivedGen

String>(200,"Good");

if (oa instanceof derivedGen)

System.out.println("superGen object is instance of derivedGen");

if (ob instanceof superGen)

System.out.println("derivedGen object is instance of superGen");

if(oa.getClass() == ob.getClass())

System.out.println("superGen class equals derivedGen class");

}

}

程序的输出结果如下:

derivedGen object is instance of superGen

从上述结果中可以看出,只有子类对象被instanceof识别为父类对象。

10.8.4 强制类型转换

和普通对象一样,泛型类的对象也可以采用强制类型转换转换成另外的泛型类型,不过只有当两者在各个方面兼容时才能这么做。

泛型类的强制类型转换的一般格式如下:

(泛型类名<实际参数>)泛型对象

下面的例子展示了两个转换,一个是正确的,一个是错误的。它使用了例10.7中的两个类:superGen和derivedGen。

【例10.11】强制类型转换示例。

//--------------文件名demoForceChange.java,程序编号10.23-----------

public class demoForceChange{

public static void main(String args[]){

superGen oa = new superGen(100);

derivedGen ob = new derivedGen

(200,"Good");

//试图将子类对象转换成父类,正确

if ((superGen)ob instanceof superGen)

·321·

Java 开发技术大全

·322· System.out.println("derivedGen object is changed to superGen");

//试图将父类对象转换成子类,错误

if ((derivedGen)oa instanceof derivedGen)

System.out.println("superGen object is changed to derivedGen");

} }

这个程序编译时会出现一个警告,如果不理会这个警告,继续运行程序,会得到下面

的结果:

derivedGen object is changed to superGen

Exception in thread "main" https://www.doczj.com/doc/606229371.html,ng.ClassCastException: superGen

at demoForceChange.main(demoForceChange.java:7) 第一个类型转换成功,而第二个则不能成功。因为oa 转换成子类对象时,无法提供足

够的类型参数。由于强制类型转换容易引起错误,所以对于泛型类的强制类型转换的限制是很严格的,即便是下面这样的转换,也不能成功:

(derivedGen)ob

因为ob 本身的第一个实际类型参数是Integer 类型,无法转换成Double 类型。 提示:建议读者如果不是十分必要,不要做强制类型转换的操作。

10.8.5 继承规则

现在再来讨论一下关于泛型类的继承规则。前面所看到的泛型类之间是通过关键字

extends 来直接继承的,这种继承关系十分的明显。不过,如果类型参数之间具有继承关系,那么对应的泛型是否也会具有相同的继承关系呢?比如,Integer 是Number 的子类,那么Generic是否是Generic的子类呢?答案是:否。比如,下面的代码将不会编译成功:

Generic oa = new Generic(100);

因为

oa 的类型不是

Generic的父类,所以这条语句无法编译通过。事实上,

无论类型参数之间是否存在联系,对应的泛型类之间都是不存在联系的。如图10.1所示。

图10.1 Generic 类之间没有继承关系

第10章泛型

这一限制看起来过于严格,但对于类型安全而言是非常必要的。

10.9 擦拭

通常,程序员不必知道有关Java编译器将源代码转换成为class文件的细节。但在使用泛型时,对此过程进行一般的了解是必要的,因为只有了解这一细节,程序员才能理解泛型的工作原理,以及一些令人惊讶的行为——如果程序员不知道,可能会认为这是错误。

Java在JDK1.5以前的版本中是没有泛型的,为了保证对以前版本的兼容,Java采用了与C++的模板完全不同的方式来处理泛型(尽管它们二者的使用看上去很相似),Java 采用的方法称为擦拭。

擦拭的工作原理是这样的:当Java代码被编译时,全部泛型类型的信息会被删除(擦拭)。也就是使用类型参数来了替换它们的限界类型,如果没有指定界限,则默认类型是Object,然后运用相应的强制转换(由类型参数来决定)以维持与类型参数的类型兼容。编译器会强制这种类型兼容。对于泛型来说,这种方法意味着在运行时不存在类型参数,它们仅仅只是一种源代码机制。

为了更好地理解泛型是如何工作的,请看下面两个例子。

【例10.12】无限界的擦拭。

//--------------文件名Gen.java,程序编号10.24-----------

//默认情况下,T是由Object限界

public class Gen{

//下面所有的T将被Object所代替

T ob;

Gen(T ob){

this.ob = ob;

}

T getOb(){

return ob;

}

}

将这个类编译完成后,在命令行输入:

javap Gen

javap是由系统提供的一个反编译命令,可以获取class文件中的信息或者是反汇编代码。该命令执行后,输出结果如下:

Compiled from "Gen.java"

public class Gen extends https://www.doczj.com/doc/606229371.html,ng.Object{

https://www.doczj.com/doc/606229371.html,ng.Object ob;

Gen(https://www.doczj.com/doc/606229371.html,ng.Object);

https://www.doczj.com/doc/606229371.html,ng.Object getOb();

·323·

实验十 泛型与集合框架

实验十泛型与集合框架 1.实验目的 1、掌握LinkedList类和Collections类提供的用于排序和查找链表中 的数据的方法 2、掌握用散列映射来存储数据 3、掌握TreeSet类的使用 2.实验内容 1、根据附录里的源代码,按照注释要求,完成代码填空,使程序能够运行 得出结果。 1)实验1 按身高排序 2)实验2 英汉小字典 3)实验3 演出节目单 4)实验4输出args[]中的单词 2、设计编写程序完成以下任务。 1)仿照实验1编写TV类,要求通过实现Comparable接口规定该类的对象的大小关系,按price值得大小确定大小关系,即电视机按其价格确定之间的大小关系。 2)从控制台输入若干个单词(输入回车结束)放入集合中,将这些单词排序后(忽略大小写)打印出来。 知识点:List接口的实现类、String常用方法 3)请使用LinkedList来模拟一个队列(先进先出的特性): (1)拥有放入对象的方法void put(Object o) (2)取出对象的方法Object get() (3)判断队列当中是否为空的方法boolean isEmpty();并且,编写测试代码,验证你的队列是否正确。 知识点:List接口的实现类LinkedList常用方法 4)在一个列表中存储以下元素:apple,grape,banana,pear (1)返回集合中的最大的和最小的元素 (2)将集合进行排序,并将排序后的结果打印在控制台上 知识点:Collections类中的方法 3.实验步骤 略 4.评分标准 1.A——内容功能完善,编程风格好,人机接口界面好; 2.B——内容功能完善,编程风格良好,人机接口界面良好;

浅谈c# 泛型类的应用分析

浅谈c# 泛型类的应用 本篇文章是对c#中泛型类的应用进行了详细的分析介绍,需要的朋友参考下 泛型类 泛型类封装不是特定于具体数据类型的操作。泛型类最常用于集合,如链接列表、哈希表、堆栈、队列、树等。像从集合中添加和移除项这样的操作都以大体上相同的方式执行,与所存储数据的类型无关。对大多集合类的操作,推荐使用.NET Framework 类库中所提供的类。 (1)泛型类可以继承具体类、封闭式构造、开放式构造基类。 复制代码代码如下: class BaseNode { } class BaseNodeGeneric { } // 继承具体类 class NodeConcrete : BaseNode { } //继承封闭式构造基类 //封闭式构造基类指基类类型参数指定具体类型 class NodeClosed : BaseNodeGeneric { } //继承开放式构造基类 //开放式构造基类指基类类型参数未指定 class NodeOpen : BaseNodeGeneric { } (2)基类类型参数必须在子类中指定实现。 复制代码代码如下: //正确 class Node1 : BaseNodeGeneric { } //错误 //在子类中未指定父类类型参数实现 class Node2 : BaseNodeGeneric {} //错误 //在子类中未指定父类类型参数实现 class Node3 : T {} class BaseNodeMultiple { } //正确 class Node4 : BaseNodeMultiple { } //正确

《集合框架及泛型》上机实践内容

《集合框架及泛型》作业 一、根据课上讲解内容,完成演示示例和课堂练习 1、ArrayList获取并打印新闻标题 需求说明:按照以下实现的步骤,使用ArrayList获取和打印新闻标题,显示效果如下图所示: (1)创建多个各类新闻标题对象,包含ID、名称和创建者三个属性; (2)创建存储各类新闻标题的集合对象; (3)按照顺序依次添加各类新闻标题,使用add()方法; (4)获取新闻标题的总数,使用size()方法; (5)根据位置获取相应新闻标题、逐条打印每条新闻标题的名称,使用for 循环遍历。 2、ArrayList存储狗狗信息 需求说明:按照以下实现的步骤,使用ArrayList存储狗狗信息,使用ArrayList的方法对狗狗信息进行删除、读取和判断,显示效果如下图所示:(1)存储多条狗信息,获取狗总数,逐条打印出各条狗信息; (2)删除指定位置的狗,使用remove()方法; (3)判断集合中是否包含指定狗,使用contains()方法;

3、LinkedList添加和删除新闻标题 需求说明:在作业1的基础上,换用LinkedList存储新闻数据,并且使用LinkedList的getFirst()和getLast()方法获取第一条和最后一条数据,以及removeFirst()和removeLast()方法删除第一条和最后一条数据,输出效果如下图所示。 4、集合头尾位置删除和条件狗信息 需求说明:按照作业3的实现方式和所用到LinkedList的方法,实现狗狗信

息的更新并输出,输出效果如图所示。 5、使用Iterator和增强型for循环遍历Set 需求说明:按照以下实现的步骤,使用Iterator和增强型for循环遍历Set,输出效果如下图所示: (1)创建多个各类新闻标题对象,包含ID、名称和创建者三个属性; (2)创建存储各类新闻标题的集合对象; (3)按照顺序依次添加各类新闻标题; (4)获取新闻标题的总数; (5)使用iterator()获取Iterator对象; (6)使用Iterator遍历集合,使用hasNext()方法作为循环条件,判断是否存在另一个可访问的元素; (7)使用增强型for遍历集合;

JAVA实验报告-集合框架与泛型机制

Java 语言程序设计 C 实验报告 集合框架及泛型机制 学生姓名 专业、班级 指导教师 成绩 计算机与信息工程学院 年月日

一、实验目的 学习课程相关章节知识,通过上机练习,掌握以下知识: 1.掌握 List 接口下 ArrayList 及 LinkedList 的使用方法。 2.掌握 Map 接口下 HashMap 及 HashTable的使用方法 3.掌握集合中泛型的使用 二、实验内容 利用集合完成象数据库那样存储数据,并且可以简单查询,利用 map 存储学生信息,字段如下: id ,name,age,实现步骤: (1)创建类,类图如下: (2)在 main 方法编写逻辑代码 (3)运行程序并测试结果 package https://www.doczj.com/doc/606229371.html,; public class Student { private String name ; private int age ; private String id ;

public String getName() { return name ; } public void setName(String name ) { this . name =name ; } public int getAge() { return age ; } public void setAge(int age ) { this. age=age ; } public String getId() { return id; } public void setId(String id) { this. id=id; } public Student(String name ,int age , String id ) { super(); this. name =name ; this. age=age ; this. id=id; } public void sayHi() { System.out.println("name=" +this.getName()+"age=" + this .getAge()+" " + "id=" + this.getId()); } }

java简答(无答案版)

1、JDBC编程的主要步骤有那些? 答:选择并加载一个合适的JDBC驱动程序; 创建一个Connection对象,建立与数据库的链接; 创建一个Statement对象,用该Statement对象进行数据库的操作; 从返回的ResultSet中获取相应数据。 2、什么是标准的输入输出流? 答:system.in 标准输入流system.out.标准输出流system.err标准错误输出 3、抽象类的特点和用途? 答:抽象类不能创建对象,只能作为其他类的父类; 抽象类的作用: 1、表示抽象的概念,如动物, 2、建立类之间的关系 3、为实现某一接口的单一功能,抽象类中的抽象方法可以不要全部实现 4、Java的GUI事件处理模型的编程步骤有那些? 5、什么是对象流和序列化? 答:对象流是一种以字节流为基础的可用于持久化保存对象的机制; 序列化是指将java对象转化为字节序列的过程 6、Java语言如何进行异常处理? 7、编写JDBC应用程序通用的步骤有哪几个? 8、什么是Java的GUI事件处理机制? 答:事件响应是GUI程序设计的关键部分,所谓的事件响应就是通过调用与事件关联的方法来处理执行事件。Java事件处理机制把事件源、事件监听器、事件对象三个基本要素联系起来了,包含了对事件进行监听、发生事件、通知监听器以及处理整个事件的流程。 9、类的重用方式有几种?分别如何实现?, 答:继承和组合;java只支持单继承,每个子类都只能有一个直接父类,子类可以继承父类的数据成员和方法,也可以增加新的变量和方法,还可以覆盖继承的方法,通过关键字extends来实现。组合也称为聚合,指java的类中可以包含其他类的对象作为数据成员,是一种包含关系。 10、如何理解Java中包的概念? 11、类的访问控制有哪几种,各有和作用? 答:public: Protected: Private: 包 12、多态机制实线的条件? 答:1、必须是继承关系的类; 2、首先要把子类对象向上转型为父类对象,然后用父类对象调用同名的方法, 系统才能通过动态绑定自动识别调用的方法所属的类; 3、对象的使用者只保持与父类接口通信。 13、终结类和终结方法的特点和用途? 14、反射机制可以用来在程序运行时得到对象的类的那些信息? 答:它允许运行中的Java程序对自身进行检查,并能直接操作程序的内部属性或方法。Reflection机制允许程序在正在执行的过程中,利用Reflection APIs取得任何已知名称的类的

实验11泛型与集合

实验内容: (1)定义一个泛型类Instrument,其中包括一个泛型方法void play(E x)。定义两种乐器类:Cello、Violin 可以进行演奏。定义一个测试类进行测试。 package ex111; public class Violin { public String toString() { System.out.println("violin........."); return ""; } } package ex111; public class Test_Main { public static void main(String[] args) { instrumentmodel = new instrument(); instrumentmodel1 = new instrument(); Cello cello = new Cello(); Violin violin = new Violin(); model.play(cello); model1.play(violin); } } package ex111; class instrument {//泛型类 void play(E x) { x.toString(); } } package ex111; public class Cello { public String toString(){ System.out.println("cello........."); return ""; } } (2)输入10 个数字保存到List 中,并按倒序显示出来。 package ex112; import java.util.ArrayList; import java.util.Collections;

实验11泛型与集合

实验内容: (1)定义一个泛型类In strume nt,其中包括一个泛型方法void play(E x) 定义两种乐器类:Cello、Violin可以进行演奏。定义一个测试类进行测试。 package ex111; public class Violin { public String toStri ng() { System.out.println("violin ....... "); return ""; } } package ex111; public class Test_Ma in { public static void main( Stri ng[] args) { instrumentmodel = new instrument(); in strume ntvVioli n> model1 = new in strume ntvVioli n>(); Cello cello = new Cello(); Violin violin = new Violi n(); model.play(cello); model1.play(violi n); } } package ex111; class in strume nt {//泛型类void play(E x) { x.toStri ng(); } } package ex111; public class Cello { public Stri ng toStri ng(){ System.out.println("cello ....... "); return ""; } } (2)输入10个数字保存到List中,并按倒序显示出来 package ex112; import java.util.ArrayList; import java.util.Collectio ns; import java.util.List; import java.util.Sca nner; public class Test{ public static void main(final String[] args) { final List< In teger> in tegers = new ArrayListvl nteger>(); final Scanner sca nner = new Scann er(System.i n); for (int i = 1; i <= 3; i++) {

看一下Java泛型的设计

从零开始来看一下Java泛型的设计 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用。本文我们将从零开始来看一下Java泛型的设计,将会涉及到通配符处理,以及让人苦恼的类型擦除。 作者:ziwenxie来源:ziwenxie|2017-03-03 10:37 收藏 分享 引言 泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用。本文我们将从零开始来看一下Java 泛型的设计,将会涉及到通配符处理,以及让人苦恼的类型擦除。 泛型基础 泛型类 我们首先定义一个简单的Box类: public class Box {

private String object; public void set(String object) { this.object = object; } public String get() { return object; } } 这是最常见的做法,这样做的一个坏处是Box里面现在只能装入String类型的元素,今后如果我们需要装入Integer 等其他类型的元素,还必须要另外重写一个Box,代码得不到复用,使用泛型可以很好的解决这个问题。 public class Box { // T stands for "Type" private T t; public void set(T t) { this.t = t; } public T get() { return t; } } 这样我们的Box类便可以得到复用,我们可以将T替换成任何我们想要的类型: Box integerBox = new Box(); Box doubleBox = new Box(); Box stringBox = new Box(); 泛型方法 看完了泛型类,接下来我们来了解一下泛型方法。声明一个泛型方法很简单,只要在返回类型前面加上一个类似的形式就行了: public class Util { public static boolean compare(Pair p1, Pair p2) { return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue()); } }

实验11泛型与集合

实验内容: (1) 定义一个泛型类Instrument<E 〉,其中包括一个泛型方法void pla y(E x)。 定义两种乐器类:Cello、Violin 可以进行演奏。定义一个测试类进行测试. package ex111; public class Violin{ publicString toString() { System、out、println("violin、、、、、、、、、"); return ””; } } package ex111; public class Test_Main{ public staticvoid main(String[]args){ instrument〈Cello>model = new instrument〈Cello〉(); instrument

6Java第六单元练习题-泛型与集合

6泛型与集合 6.1单项选择题 1.可实现有序对象的操作是?() A.HashMap B.HashSet C.TreeMap D.Stack 2.不是迭代器接口(Iterator)所定义的方法是()。 A.hasNext() B.next() C.remove() D.nextElement() 3.下面说法不正确的是() A.列表(List)、集合(Set)和映射(Map)都是java.util包中的接口。 B.List接口是可以包含重复元素的有序集合。 C.Set接口是不包含重复元素的集合。 D.Map接口将键映射到值,键可以重复,但每个键最多只能映射一个值。 4.下面那些方法不是接口Collection中已声明的方法() A.添加元素的add(Object obj) 方法 B.删除元素的remove(Object obj)方法 C.得到元素个数的length()方法 D.返回迭代器的iterator()方法,迭代器用于元素遍历 5. 下列关于容器的描述中,错误的是() A.容器是由若干个组建和容器组成的 B.容器是对图形界面中界面元素的一种管理 C.容器是一种对指定宽和高的矩形范围 D.容器都是可以独立的窗口 6. 下列界面元素中,不是容器的是() A.List B.JFrame C.JDialog D.Panel 7.应用程序的main方法中有以下语句,则输出的结果是( )。

Hashtable hashtable=new Hashtable(); hashtable.put("100","aaa"); hashtable.put("200","bbb"); hashtable.put("300","ccc"); System.out.println(hashtable.get("300").toString() + hashtable.get("200").toString() + hashtable.get("100").toString()); A) aaa B) bbb C) ccc D) cccbbbaaa 6.2判断题 1.Map接口是自Collection接口继承而来。(×) 2.集合Set是通过键-值对的方式来存储对象的。(×) 3.Integer i = (Integer.valueOf("926")).intValue();(√) 4.String s = (Double.valueOf("3.1415926")).toString(); (√) 5.Integer I = Integer.parseInt("926");(√) 6.Arrays类主要对数组进行操作。(√) 7.在集合中元素类型必须是相同的。(√) 8.集合中可以包含相同的对象。(×) 9.枚举接口定义了具有删除功能的方法。(×) 6.3程序阅读题 1.阅读下面的程序,回答问题。 import java.util.*; public class T { public static void main(String args[]) { Set set = new TreeSet(); set.add(new Integer(10)); set.add(new Integer(5)); set.add(new Integer(15)); set.add(new Integer(5)); set.add(new Integer(10)); System.out.println("size = " + set.size()); Iterator it=set.iterator(); while(it.hasNext()){ System.out.print(it.next()+" "); } }

实验七:Java集合与泛型

实验七Java集合与泛型 一、实验目的 1)掌握集合的概念、体系结构、分类及使用场景 2)了解Set接口及主要实现类(HashSet、TreeSet) 3)了解List接口及主要实现类(ArrayList、LinkedList、Vector) 4)掌握ArrayList的使用 5)掌握ArrayList与Vector的区别 6)了解Map接口及主要实现类(HashMap、TreeMap、HashTable) 7)掌握HashMap的使用 8)掌握HashMap与HashTable的区别 二、实验环境 JDK1.6+Eclpise3.2 三、实验准备 1)复习课件中理论知识 2)练习课堂所讲的例子 四、实验内容 1、编写程序练习List集合的基本使用: 1) 创建一个只能容纳String对象名为names的ArrayList集合; 2)按顺序往集合中添加5个字符串对象:“张三”、“李四”、“王五”、“马六”、“赵七”; 3)对集合进行遍历,分别打印集合中的每个元素的位置与内容; 4)首先打印集合的大小,然后删除集合中的第3个元素,并显示删除元素的内容,然后再打印目前集合中第3个元素的内容,并再次打印集合的大小。 2、编写程序练习Map集合的基本使用: 1)创建一个只能值只能容纳String对象的person的HashMap集合; 2)往集合中添加5个“键-值”对象:id—>”1”、name—>”张三”、sex—>”男”、age—>”25”、love—>”爱学Java” 3)对集合进行遍历,分别打印集合中的每个元素的键与值; 4)首先打印集合的大小,然后删除集合中的键为age的元素,并显示删除元素的内容,并再次打印集合的大小。 五、验过程及结果 第1题调试结果如下图:

java泛型接口,泛型类泛型方法

泛型可提高代码的高扩展性和重用率. 1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。 2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。 3、泛型的类型参数可以有多个。 4、泛型的参数类型可以使用extends语句,例如。习惯上称为“有界类型”。 5、泛型的参数类型还可以是通配符类型。例如Class classType = Class.forName("https://www.doczj.com/doc/606229371.html,ng.String"); 泛型可以用在接口,类方法,集合上面. 泛型接口: interface testGenerics{ T getT(T t); String assume(T t); } 泛型类:

public class GenericsFoo { private T x; public GenericsFoo(T x) { this.x = x; } public T getX() { return x; } public void setX(T x) { this.x = x; } } 使用来声明一个类型持有者名称,然后就可以把T当作一个类型代表来声明成员、参数和返回值类型。 当然T仅仅是个名字,这个名字可以自行定义。 泛型方法: 是否拥有泛型方法,与其所在的类是否泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值前。如: public class ExampleA { public void f(T x) {

System.out.println(x.getClass().getName()); } 使用泛型方法时,不必指明参数类型,编译器会自己找出具体的类型。泛型方法除了定义不同,调用就像普通方法一样。 限制泛型的可用类型: 在上面的例子中,由于没有限制class GenericsFoo类型持有者T的范围,实际上这里的限定类型相当于Object,这和“Object泛型”实质是一样的。限制比如我们要限制T为集合接口类型。只需要这么做: class GenericsFoo,这样类中的泛型T 只能是Collection接口的实现类,传入非Collection接口编译会出错。 注意:这里的限定使用关键字extends,后面可以是类也可以是接口。但这里的extends已经不是继承的含义了,应该理解为T类型是实现Collection接口的类型,或者T是继承了XX类的类型。 下面继续对上面的例子改进,我只要实现了集合接口的类型: public class CollectionGenFoo { private T x; public CollectionGenFoo(T x) { this.x = x;

学号姓名--集合框架与泛型实验报告

浙江大学城市学院实验报告 课程名称面向对象程序设计 实验项目名称集合框架与泛型 学生姓名专业班级学号 一. 实验目的和要求 1. 了解Java集合框架的接口和实现类 2. 理解泛型类、泛型接口、泛型方法的特点 3. 掌握List接口及其实现类LinkedList、ArrayList 4. 了解Set接口及其实现类HashSet、TreeSet 5. 了解Map及其实现类HashMap、TreeMap 二. 实验内容 1. 分析Java集合框架的接口和实现类的组成 2. 分析泛型类、泛型接口、泛型方法的特点 3. 编程实现:设计学生管理类StudentManager(用List集合管理学生对象) 4. 选作-编程实现:设计学生管理类StudentManager(用Set集合管理学生对象) 5. 选作-编程实现:设计学生管理类StudentManager(用Map管理学生对象) 三. 实验结果与分析(可将程序运行结果截屏,也可分析运行结果) 1. 分析Java集合框架的接口和实现类的组成 请查阅书籍和Java帮助文档,说明Java集合框架的接口组成以及它们的继承关系,并针对每个接口给出具体的实现类。 答: 2. 分析泛型类、泛型接口、泛型方法的特点 请查阅书籍和Java帮助文档,举例说明泛型类、泛型接口、泛型方法的特点。 答: 3. 编程实现:设计学生管理类StudentManager(用List集合管理学生对象)。 StudentManager类的功能包括添加学生、查询学生、删除学生、统计学生成绩等。需要设计表示学生对象的Student类,并用LinkedList或ArrayList集合来管理可被数量的学生对象。另外还需要设计测试类Test来验证StudentManager的功能。 4. 编程实现:设计学生管理类StudentManager(用Set集合管理学生对象)。具体功能 要求同第3题,但是需要用Set的实现类(比如HashSet、TreeSet)管理学生对象。

实验二 泛型异常处理 实验报告

实验二泛型异常处理实验报告 一、实验目的 1. 理解使用泛型、枚举的目的。 2. 掌握异常类、异常类的结构及自定义异常类的编程方法。 二、实验要求 1. 应用泛型编写简单的应用程序; 2. 自定义异常类的编写; 3. 异常类中try, catch, throw, throws 的编写方法。 三、实验内容 1. 阅读以下程序,写出运行结果。同时,把程序改写为使用泛型的程序。对比两个程序并说出泛型的优点。 public class GenericityDemo { public static void main(String[] args) { // 定义类Genericity的一个Integer版本 Genericity intobj = new Genericity(new Integer(88)); intobj.showType(); int i = (Integer) intobj.getobj(); System.out.println("value= " + i); System.out.println("----------------------------------"); // 定义类Genericity的一个String版本 Genericity strobj = new Genericity("Hello Genericity!"); strobj.showType(); String s = (String) strobj.getobj(); System.out.println("value= " + s); } } class Genericity { private Object obj; // 定义通用类型成员 public Genericity(Object obj) { this.obj = obj; } public Object getobj() { return obj;} public void setobj(Object obj) {this.obj = obj;} public void showType() { System.out.println("T实际类型是: " + obj.getClass().getName()); } }

实验五 集合类与泛型

实验五集合类与泛型 1.实验目的 (1)掌握ArrayList类与LinkedList类的用法; (2)掌握TreeSet类的用法; (3)掌握Map接口及其实现类的用法 (4)掌握泛型的用法。 2.实验内容 实验题1 有四个类,主类Store在包https://www.doczj.com/doc/606229371.html,.nwsuaf.jp.p4中,Mobile、Mp3Player、Product在包https://www.doczj.com/doc/606229371.html,.nwsuaf.jp.p4.data中,Mobile、Mp3Player是Product的子类, Product类实现Comparable接口,重写了Comparable接口中方法compareTo,实现了product对象按照价格排序。 基本要求: (1)在主类Store中实例化多个Mobile类与Mp3Player的实例,分别将这些实例用ArrayList与LinkedList存放,最后用StringBuiler存放并将其输出。 (2)用迭代器(iterator)将实例对象输出(要求用加强型for循环)。 Store类 package https://www.doczj.com/doc/606229371.html,.nwsuaf.jp.p44; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.swing.JOptionPane;

import https://www.doczj.com/doc/606229371.html,.nwsuaf.jp.p4.data.Mobile; import https://www.doczj.com/doc/606229371.html,.nwsuaf.jp.p4.data.Mp3Player; import https://www.doczj.com/doc/606229371.html,.nwsuaf.jp.p4.data.Product; public class Store { public static int count=0; public static void main(String[] args) { Mobile m1=new Mobile("E365 on china Mobile",1780); Mobile m2=new Mobile("E3330 on china Mobile",1450); Mp3Player mp1=new Mp3Player("Meiz0 X3 (256MB)",399); Mp3Player mp2=new Mp3Player("Meiz0 E5 (512MB)",580); Mp3Player mp3=new Mp3Player("Xlive XM Mp3Player(256MB)",930); Product p[]={mp2,mp1,mp3,m1,m2}; Arrays.sort(p); StringBuilder text=new StringBuilder(); for(Product i:p){ text.append(i).append("\n"); } System.out.println("用StringBuiler输出:\n"+text); List list1=new ArrayList();//用到泛型因为Product是mp3和mobil的总类,ArrayList只能放一种类型 List list2=new LinkedList(); for(int i=0;i

集合及泛型自测题

选择题 1.在java中,( C )对象可以使用键/值的形式保存数据。 A:ArrayList B: HashSet C: HashMap D: LinkedList 2.创建一个类,来存储唯一的对象元素,元素不需要保持特定顺序,但是必须唯一。最 能满足这种要求的接口是:( AE ) 。(多选) A、Set B、List C、Map D、Array E、HashSet 3.以下不属于ArrayList的方法的是(C ) A: add() B: addAll() C: addFirst() D: size(); 4.下列声明语句错误的是(C ) A : List list=new ArrayList() B: List list=new LinkedList(); C: ArrayList al= new List(); D: Set set=(Set)new ArrayList() 5.欲构造ArrayList 类的一个实例,此类继承了List 接口,下列哪个方法是正确的(B )。 A: ArrayList myList=new Object( ) B: List myList=new ArrayList( ) C: ArrayList myList=new List( ) D: List myList=new List( ) 填空题 6.Iterator通过调用hasNext() 判断是否有下一元素,并通过next() 方法取得 下一元素。 判断题 7.ArrayList 和LinkedList 都实现Cloneable 接口,都提供了两个构造函数,一个无参 的,一个接受另一个Collection。(√) 8.Map接口不是Collection接口的继承(√)

JAVA实验报告-集合框架及泛型机制

Java语言程序设计C 实验报告 集合框架及泛型机制 学生姓名 专业、班级 指导教师 成绩 计算机与信息工程学院 年月日 一、实验目的 学习课程相关章节知识,通过上机练习,掌握以下知识:

1.掌握List接口下ArrayList及LinkedList的使用方法。 2.掌握Map接口下HashMap 及HashTable的使用方法 3.掌握集合中泛型的使用 二、实验内容 利用集合完成象数据库那样存储数据,并且可以简单查询,利用map存储学生信息,字段如下: id ,name,age,实现步骤: (1)创建类,类图如下: (2)在main方法编写逻辑代码 (3)运行程序并测试结果 package com、cn; public class Student { private String name; private int age; private String id; public String getName() { return name; } public void setName(String name) {

this、name = name; } public int getAge() { return age; } public void setAge(int age) { this、age = age; } public String getId() { return id; } public void setId(String id) { this、id = id; } public Student(String name, int age, String id) { super(); this、name = name; this、age = age; this、id = id; } public void sayHi() { System、out、println("name="+this、getName()+"age="+this、getAge()+" "+"id="+this、getId()); } } //Databace类 package com、cn; import java、util、Collection; import java、util、HashMap; import java、util、Iterator; public class Databace { private Student a; public Databace() { super(); map=new HashMap(); } public Student getA() { return a; }

集合实验

Java集合与泛型 一、实验目的 1)掌握集合的概念、体系结构、分类及使用场景2)了解Set接口及主要实现类(HashSet、TreeSet) 3)了解List接口及主要实现类(ArrayList、LinkedList、Vector)4)掌握ArrayList的使用5)掌握ArrayList与Vector的区别 6)了解Map接口及主要实现类(HashMap、TreeMap、HashTable)7)掌握HashMap的使用 8)掌握HashMap与HashTable的区别 二、实验环境 JDK1.6+Eclpise3.2 三、实验准备 1)复习课件中理论知识2)练习课堂所讲的例子 四、实验内容 1、编写程序练习List集合的基本使用: 1) 创建一个只能容纳String对象名为names的ArrayList集合; 2)按顺序往集合中添加5个字符串对象:“张三”、“李四”、“王五”、“马六”、“赵七”;3)对集合进行遍历,分别打印集合中的每个元素的位置与内容; 4)首先打印集合的大小,然后删除集合中的第3个元素,并显示删除元素的内容,然后再打印目前集合中第3个元素的内容,并再次打印集合的大小。 2、编写程序练习Map集合的基本使用: 1)创建一个只能值只能容纳String对象的person的HashMap集合; 2)往集合中添加5个“键-值”对象:id—>”1”、name—>”张三”、sex—>”男”、age —>”25”、love—>”爱学Java” 3)对集合进行遍历,分别打印集合中的每个元素的键与值; 4)首先打印集合的大小,然后删除集合中的键为age的元素,并显示删除元素的内容,并再次打印集合的大小。

实验6 泛型与集合框架_附答案

任务一:用LinkedList存放对象 1.利用面向对象的思想,创建以下类: ●Person类,包含Person的姓名和身份证号码,覆盖Object类的toString() 方法,显示“姓名:XXX 身份证号:XXX”。 ●Student类,继承Person类,包含学生的语文、数学、英文课的成绩,并覆盖 父类的toString()方法,显示“姓名:XXX 身份证号:XXX 语文:XXX 数学:XXX 英文:XXX”。 ●Teacher类,继承Person类,包含教师的工资。并覆盖父类的toString()方 法,显示“姓名:XXX 身份证号:XXX 工资:XXX”。 ●public class Person implements Comparable{ ●String name; ●String ID; ●Person(String s,String i){ ●name=s; ●ID=i; } ●public String toString() { ●String str="姓名:"+name+" 身份证号码:"+ID; ●return str; } ●public int compareTo(Object arg0) { ●Person p=(Person)arg0; ●return https://www.doczj.com/doc/606229371.html,pareTo(p.ID); } } ●class Student extends Person { ●int Chinese; ●int Math; ●int English; ●Student(String n,String i,int c,int m,int e){ ●super(n,i); ●Chinese=c; ●Math=m; ●English=e; } ●public String toString() { ●String str; ●str=" 语文成绩:"+Chinese+" 数学成绩:"+Math+" 英语成绩: "+English; ●return super.toString()+str; ●} ●} ●class Teacher extends Person{ ●int salary; ●Teacher(String n,String i,int s){ ●super(n,i); ●salary=s; ●}

文本预览