文档库 最新最全的文档下载
当前位置:文档库 › JAVA泛型详解

JAVA泛型详解

JAVA泛型详解
JAVA泛型详解

7.*泛型

我们知道,使用变量之前要定义,定义一个变量时必须要指明它的数据类型,什么样的数据类型赋什么样的值。下面我们先来看一个例子:

假如我们现在要定义一个类来表示坐标,要求坐标的数据类型可以是整数、小数和字符串,例如:

x=50,y=50

x=37.13,y=169.35

x="东经100度",y="北纬100度"

针对不同的数据类型,除了借助方法重载,还可以借助自动封装和向上转型。我们知道,基本数据类型可以自动装箱,被转换成对应的包装类;Object是所有类的父类,任何一个类的实例都可以向上转型为Object类型。

程序清单:

1.public class DemoTest{

2.public static void main(String[]args){

3.Position p=new Position();

4.p.setX(50);//int->Integer->Object

5.p.setY(50);

6.int x=(Integer)p.getX();//向下转型

7.int y=(Integer)p.getY();

8.System.out.println("This Position is:"+x+","+y);

9.p.setX(37.13);//double->Double->Object

10.p.setY("东经100度");

11.double m=(Double)p.getX();//必须向下转型

12.double n=(Double)p.getY();//运行期间抛出异常

13.System.out.println("This Position is:"+m+","+n);

14.}

15.}

16.class Position{

17.Object x=0;

18.Object y=0;

19.//由于篇幅原因,这里省去setter和getter方法,请读者实验时自行加上

20.}

程序说明:上面的代码中,生成坐标时不会有任何问题,但是取出坐标时,要向下转型,但是向下转型存在着风险,而且编译期间不容易发现,只有在运行期间才会抛出异常,所以要尽量避免使用向下转型。运行上面的代码,第12行会抛出https://www.wendangku.net/doc/0b10376729.html,ng.ClassCastException 异常。那么,有没有更好的办法,既可以不使用重载(有重复代码),又能把风险降到最低呢?有,可以使用泛型类,它可以接受任意类型的数据。所谓“泛型”,就是“宽泛的数据类型”,任意的数据类型。

泛型(Generic type或者generics)是对Java语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。可以在集合框架(Collection framework)中看到泛型的动机。

Java语言中引入泛型是一个较大的功能增强﹐不仅语言、类型系统和编译器有了较大

的变化,以支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了。这带来了很多好处:

1.类型安全。泛型的主要目标是提高Java程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。

2.类型约束。Java程序中的一种流行技术是定义这样的集合,即它的元素或键是公共类型的,比如“String列表”或者“String到String的映射”。通过在变量声明中捕获这一附加的类型信息,泛型允许编译器实施这些附加的类型约束。类型错误现在就可以在编译时被捕获了,而不是在运行时当作ClassCastException被抛出来。将类型检查从运行时挪到编译时有助于您更容易找到错误,并可提高程序的可靠性。

3.消除强制类型转换。泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。

7.*.1泛型类与泛型接口

1.泛型类

在定义带类型参数的类时,在紧跟类命之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取值范围进行限定,多个类型参数之间用“,”号分隔。定义完类型参数后,可以在定义位置之后的类的几乎任意地方(静态块,静态属性,静态方法除外)使用类型参数,就像使用普通的类型一样。

我们更改上面的代码,使用泛型类:

程序清单:

1.public class DemoTest{

2.public static void main(String[]args){

3.//实例化泛型类

4.Positionp1=new Position();

5.p1.setX(50);

6.p1.setY(50);

7.int x=p1.getX();

8.int y=p1.getY();

9.System.out.println("This Position is:"+x+","+y);

10.Positionp2=new Position();

11.p2.setX(37.13);

12.p2.setY("东经100度");

13.double m=p2.getX();

14.String n=p2.getY();

15.System.out.println("This Position is:"+m+","+n);

16.}

17.}

18.//定义泛型类

19.class Position{

20.T1x;

21.T2y;

22.//由于篇幅原因,这里省去setter和getter方法,请读者实验室自行加上

23.}

This Position is:50,50

This Position is:37.13,东经100度

程序说明:与普通类的定义相比,上面的代码在类名后面多出了,T1,T2是自定义的标识符,也是参数,用来传递数据的类型,而不是数据的值,我们称之为类型参数。在泛型中,不但数据的值可以通过参数传递,数据的类型也可以通过参数传递。T1,T2只是数据类型的占位符,运行时会被替换为真正的数据类型。类型参数(泛型参数)由尖括号包围,多个参数由英文逗号分隔,如。类型参数需要在类名后面给出。一旦给出了类型参数,就可以在类中使用了。类型参数必须是一个合法的标识符,习惯上使用单个大写字母,通常情况下,K表示键,V表示值,E表示异常或错误,T表示一般意义上的数据类型。

泛型类在实例化时必须指出具体的类型,也就是向类型参数传值,格式为:

className variable=new className();

也可以省略等号右边的数据类型,但是会产生警告,即:

className variable=new className();

因为在使用泛型类时指明了数据类型,赋给其他类型的值会抛出异常,既不需要向下转型,也没有潜在的风险,比自动封装和向上转型要更加实用。

注意:

1、泛型是Java1.5的新增特性,它以C++模板为参照,本质是参数化类型(Parameterized Type)的应用。

2、类型参数只能用来表示引用类型,不能用来表示基本类型,如int、double、char等。但是传递基本类型不会报错,因为它们会自动封装成对应的包装类。

2.泛型接口

在Java中也可以定义泛型接口,这里不再赘述,仅仅给出示例代码:

程序清单:

1.public class DemoTest{

2.public static void main(String arsg[]){

https://www.wendangku.net/doc/0b10376729.html,obj=new InfoImp("https://www.wendangku.net/doc/0b10376729.html,");

4.System.out.println("Length Of String:"+obj.getVar().length());

5.}

6.}

7.//定义泛型接口

8.interface Info{

9.public T getVar();

10.}

11.//实现接口

12.class InfoImpimplements Info{

13.private T var;

14.//定义泛型构造方法

15.public InfoImp(T var){

16.this.setVar(var);

17.}//由于篇幅原因,省去了setter和getter方法,请读者加上

18.}

Length Of String:18

7.*.2泛型方法

1.泛型方法的使用

除了定义泛型类,还可以定义泛型方法,在定义带类型参数的方法时,在紧跟可见范围修饰(例如public)之后的<>内,指定一个或多个类型参数的名字,同时也可以对类型参数的取值范围进行限定,多个类型参数之间用“,”号分隔。定义完类型参数后,可以在定义位置之后的方法的任意地方使用类型参数,就像使用普通的类型一样。

例如,定义一个打印坐标的泛型方法。

程序清单:

1.public class DemoTest{

2.public static void main(String[]args){

3.//实例化泛型类

4.Positionp1=new Position();

5.p1.setX(52);

6.p1.setY(50);

7.p1.printPosition(p1.getX(),p1.getY());

8.Positionp2=new Position();

9.p2.setX(37.13);

10.p2.setY("东经100度");

11.p2.printPosition(p2.getX(),p2.getY());

12.}

13.}

14.//定义泛型类

15.class Position{

16.T1x;

17.T2y;//由于篇幅原因,省去了setter和getter方法,请读者加上

18.//定义泛型方法

19.publicvoid printPosition(T1x,T2y){

20.T1m=x;

21.T2n=y;

22.System.out.println("This Position is:"+m+","+n);

23.}

24.}

输出结果:

This Position is:50,50

This Position is:37.13,东经100度

程序说明:上面的代码中定义了一个泛型方法printPosition(),既有普通参数,也有类型参数,类型参数需要放在修饰符后面、返回值类型前面。一旦定义了类型参数,就可以在参数列表、方法体和返回值类型中使用了。与使用泛型类不同,使用泛型方法时不必指明参数类型,编译器会根据传递的参数自动查找出具体的类型。泛型方法除了定义不同,调用就像普通方法一样。

注意:泛型方法与泛型类没有必然的联系,泛型方法有自己的类型参数,在普通类中也

可以定义泛型方法。泛型方法printPosition()中的类型参数T1,T2与泛型类Position中的T1,T2没有必然的联系,也可以使用其他的标识符代替:

1.public staticvoid printPosition(V1x,V2y){

2.V1m=x;

3.V2n=y;

4.System.out.println("This Position is:"+m+","+n);

5.}

2、限制泛型

在上面的代码中,类型参数可以接受任意的数据类型,只要它是被定义过的。但是,很多时候我们只需要一部分数据类型就够了,用户传递其他数据类型可能会引起错误。例如,编写一个泛型函数用于返回不同类型数组(Integer数组、Double数组、Character数组等)中的最大值。

程序清单:

1.publicT getMax(T array[]){

2.T max=null;

3.for(T element:array){

4.max=element.doubleValue()>max.doubleValue()?element:max;

5.}

6.return max;

7.}

程序说明:这段代码会报错,因为doubleValue()是Number类的方法,不是所有的类都有该方法,所以我们要限制类型参数T,让它只能接受Number及其子类(Integer、Double、Character等)。通过extends关键字可以限制泛型的类型,改进上面的代码:程序清单:

1.publicT getMax(T array[]){

2.T max=null;

3.for(T element:array){

4.max=element.doubleValue()>max.doubleValue()?element:max;

5.}

6.return max;

7.}

程序说明:表示T只接受Number及其子类,传入其他类型的数据会报错。这里的限定使用关键字extends,后面可以是类也可以是接口。但这里的extends 已经不是继承的含义了,应该理解为T是继承自Number类的类型,或者T是实现了某些接口的类型。

虽然Java泛型简单的用extends统一的表示了原有的extends和implements的概念,但仍要遵循应用的体系,Java只能继承一个类,但可以实现多个接口,所以你的某个类型需要用extends限定,且有多种类型的时候,只能存在一个是类,并且类写在第一位,接口列在后面,也就是:

这里的例子仅演示了泛型方法的类型限定,对于泛型类中类型参数的限制用完全一样的规则,只是加在类声明的头部,如:

1.public class DemoTest{

2.//T类型就可以用Comparable声明的方法和Seriablizable所拥有的特性了

3.}

7.*.3泛型擦除与泛型数组

1、泛型擦除

考虑如下代码的输出。

程序清单:

1.public class DemoTest{

2.public static void main(String[]args){

3.Class c1=new ArrayList().getClass();

4.Class c2=new ArrayList().getClass();

5.System.out.println(c1==c2);

6.}

7.}

程序说明:输出结果很神奇的显示:true。这是为什么呢?

为了解释清楚原因,在此不得不补充一个程序。

程序清单:

1.public class DemoTest{

2.public static void main(String[]args){

3.Listc1=new ArrayList();

4.Listc2=new ArrayList();

5.System.out.println(Arrays.toString(c1.getClass().getTypeParameters()));

6.System.out.println(Arrays.toString(c2.getClass().getTypeParameters()));

7.}

8.}

输出结果:

[E]

[E]

程序说明:根据JDK文档的描述,第5、6行Class.getTypeParameters()将返回一个TypeVariable对象的数组,表示有泛型所声明的类型参数。但是,正如从输出中所看到的,能够发现的只是用作参数占位符的标示符,而这不是有用的信息。因此残酷的现实是:在泛型代码的内部,无法获得任何有关泛型参数类型的信息。

因此,可以知道诸如类型参数标示符和泛型类型边界这类信息,但却无法知道用来创建某个特定实例的实际的类型参数。Java中的泛型是使用擦除来实现的,所以在使用泛型的时候,任何具体的类型信息都被擦除了,你唯一知道的就是知道当前使用的是一个对象。因此List和List在运行时事实上是相同的类型。这两种形式都被擦除成为他们的“原生”类型,即List。

泛型是JDK1.5才出现的,所以为了兼容,采用了擦除的方式实现。泛型类型只有在静态类型检查期间才出现,在此之后,程序中所有泛型类型都被擦除,替换为他们的非泛型上界。例如List将被擦除为List,而普通的类型变量在未指定边界的情况下将被擦除为Object

类型。

2、泛型数组

如上文所示,擦除丢失了在泛型代码中执行某些操作的能力。任何在运行时需要知道确切类型信息的操作都将无法工作。

程序清单:

1.class DemoTest{

2.private static final int SIZE=100;

3.public static void f(Object arg){

4.T var=new T();//编译错误

5.T[]array=new T[SIZE];//编译错误

6.T[]array=(T)new Object[SIZE];//编译错误

7.}

8.}

程序说明:在这段程序中,第4、5、6行均不能编译通过,可见不能直接创建真正意义上的泛型数组(之所以这么说是因为Java可以声明一个泛型数组,但是却永远不能真正创建一个泛型数组类型的对象)。

7.*.4通配符

在本节的前面的部分里已经说过了泛型类型的子类型的不相关性。即Apple是Fruit的子类,然而List却不是List的子类。但有些时候,我们希望能够像使用普通类型那样使用泛型类型:

向上转型一个泛型对象

向下转型一个泛型对象

由于泛型子类型的不相关性,原本有继承关系的对象不能像普通的类型那样向上转型。例如:

程序清单:

1.Listapples0=new ArrayList();

2.Listfruits0=new ArrayList();

3.fruits0=apples0;//无法赋值

4.apples0=fruits0;//无法赋值

5.apples0=(List)fruits0;//无法赋值

6.fruits0=(List)apples0;//无法赋值

程序说明:如程序第3-6行代码所示,无法将持有Apple的list对象赋值给持有Fruit

的list的引用,即无法向上转型;也无法将持有Fruit对象的List对象赋值给持有Apple对象的List引用;即使是进行强制类型转换也无法进行向上或者向下转型。

1、无界通配符“?”

为了使泛型的子类型仍然具有相关性,也即是在使用了泛型后依然能够保证继承关系,我们可以使用通配符“?”达到目的。

程序清单:

1.Listapples3=new ArrayList();

2.Listfruits3=new ArrayList();

3.Listcats=new ArrayList(){};

4.fruits3=apples3;

5.apples3=fruits3;

6.cats=fruits3;//

7.fruits3.add(new Apple());//无法赋值

8.apples3.add(new Apple());//无法赋值

程序说明:使用了通配符之后,就可以实现向上转型或向下转型了,也即可以将持有Apple的List对象赋值给持有Fruit的List引用了,反之也可行。然而第6行代码显示,因为使用了通配符,甚至可以将一个持有Fruit的List对象赋值给一个持有Cat的List引用。Fruit 与Cat显然并没有继承关系,那么为什么会出现这种我们不期待的情况呢?原来是因为List在功能上等价于List,这意味着编译器在运行中会将泛型擦出到Object上界,也即List能够接受某种特定类型的List,这个类型只要是Object 类型本身或Object子类就可以。既然如此,编译器就不会检查List中容纳的到底是什么类型的对象了。

从代码中可以看出,由List声明的List可以容纳任意某种特定的类型的List对象。因此给List初始化之后就不能够再往里添加任何类型的对象了,哪怕是Object的对象也不可以,但是可以添加null。同时,这也是List与List的区别,List只能容纳一种类型的对象,是一个同构集合;但是List可以容纳任意Object类型或Object子类的对象,是一个异构集合。

2、通配符上界“?extends T”

很显然,Fruit与Cat并没有继承关系,但是用List仍然了可以把一个List的对象赋值给List类型的引用,这并不是我们所期待的,那么能不能有一种机制能够使List类型的对象只能赋值给他类型相关的引用呢?答案是:能。这就是

程序清单:

1.Listapples1=new ArrayList();

2.Listfruits1=new ArrayList();

3.Listcats1=new ArrayList();

4.fruits1=apples1;

5.fruits1=cats1;//无法赋值

6.fruits1.add(new Apple());//无法赋值

7.apples1=fruits1;//无法赋值

8.apples1=(List)fruits1;//ok

程序说明:程序第2行,“?extends Fruit”表示可以接受的类型的上界为Fruit类型,也即只能接受Fruit类型或Fruit的子类型,例如程序的第4行,List类型的引用接受了一个List类型的对象,编译通过。有了“?extends T”,就确保了只能接受T 或T的子类(向上转型),Cat不能Fruit的子类,因此程序的第5行代码不能通过编译。程序的第6行不能通过编译,这是因为“?extends T”通配符告诉编译器在处理一个类型为T 或者T的子类型,但是不知道这个子类型究竟是什么。因为没法确定,为了保证类型安全,就不允许往里面加入任何类型的数据,除了null。程序的第8行,通过向下转型,可以将先前向上转型为List类型的对象重新向下转换为List类型的对象。

既然“?extends T”的机制的实现的是向上转型,那么它更大用处应该用在方法传参上面,而不是向上转型为一个不能改变的对象。假设有以下的类继承关系:一个抽象类Shape,包含一个抽象方法draw,非抽象类Circle、Rectangle都继承了Shape,并且实现了draw方法,有一个drawAll方法想要能够画出所有类的图形。

程序清单:

1.public void drawAll(Listshapes){

2.for(Shape s:shapes){

3.s.draw(this);

4.}

5.}

程序说明:类型规则导致drawAll方法只能接受持有Shape对象的List。然而我们想要的是使drawAll方法能够接受持有Circle或者持有Rectangle对象的List,即接受任何一个持有Shape类型或者其子类的对象的List。很遗憾的是上面的程序并不能达到此目的,但是有了“?extends T”就可以实现了。

程序清单:

1.public void drawAll(Listshapes){

2.for(Shape s:shapes){

3.s.draw(this);

4.}

5.}

程序说明:改写drawAll方法将List替换成List,现在drawAll 能够接受任何持有Shape及其子类对象的List了,也就完美地实现我们想要达到的目的。3、通配符下界“?super T”

与“?extends T”相对的是通配符下界“?Super T”

程序清单:

1.Listfruits2=new ArrayList();

2.Listapples2=new ArrayList();

3.apples2=fruits2;

4.apples2.add(new Apple());ok

5.apples2.add(new Fruit());//无法赋值

程序说明:“?super Apple”表示可以接受的类型的下界为Apple类型,也即只能接受Apple类型或者Apple的父类型,如程序的第3行。同样因为编译器不知道具体是什么类型,但是可以确定的是一定是Apple类型或者是Apple的父类型,所以可以向List中添加Apple类型,但是不可以添加Apple的父类型,例如程序的第4、5行。

List大多用于方法的传参,因为编译器不能确定传入的到底是什么类型,由于Object是所有类的子类,所以方法的返回值统一都是Object类型。但有时候我们期望返回值是我们期望的类型,这个时候“?super T”就派上用场了。一个常见的例子用于对象的比较,例如:Fruit类实现了Comparable接口,即Fruit类的对象时可以进行排序的,但是我们希望所有继承Fruit类的对象也能够进行排序。

程序清单1:

1.public static>ListhigherPrice(ListA){

2.Collections.sort(A);

3.return A;

4.}

程序清单2:

1.Listlist4=higherPrice(list0);//list0为持有Fruit类型的List对象

2.Listlist6=higherPrice(list1);//list1为持有Apple类型的List对象

程序分析:在方法返回值前加上“?super T”对方法进行限制就能返回特定类型的List 对象了。

java泛型详解

java泛型详解 泛型(Generic type 或者generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。 可以在集合框架(Collection framework)中看到泛型的动机。例如,Map类允许您向一个Map添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如String)的对象。 因为Map.get()被定义为返回Object,所以一般必须将Map.get()的结果强制类型转换为期望的类型,如下面的代码所示: Map m = new HashMap(); m.put("key", "blarg"); String s = (String) m.get("key"); 要让程序通过编译,必须将get()的结果强制类型转换为String,并且希望结果真的是一个String。但是有可能某人已经在该映射中保存了不是String的东西,这样的话,上面的代码将会抛出ClassCastException。 理想情况下,您可能会得出这样一个观点,即m是一个Map,它将String键映射到String值。这可以让您消除代码中的强制类型转换,同时获得一个附加的类型检查层,该检查层可以防止有人将错误类型的键或值保存在集合中。这就是泛型所做的工作。 泛型的好处 Java 语言中引入泛型是一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了。这带来了很多好处: · 类型安全。泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。 Java 程序中的一种流行技术是定义这样的集合,即它的元素或键是公共类型的,比如“Str ing列表”或者“String到String的映射”。通过在变量声明中捕获这一附加的类型信息,泛型允许编译器实施这些附加的类型约束。类型错误现在就可以在编译时被捕获了,而不是在运行时当作 ClassCastException展示出来。将类型检查从运行时挪到编译时有助于您更容易找到错误,并可提高程序的可靠性。

看一下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()); } }

java泛型

Java为什么要有泛型? 答: ①在没有泛型之前,一旦把一个对象添加到java集合中去,集合就会忘记对象的类型,把所有 的对象当成Object来处理.当程序从集合中取出对象后,就需要进行强制转换类型.这种类型转换不仅代码臃肿而且容易引起ClassCastException异常. ②增加了泛型支持后的集合,完全可以记住集合中的元素类型,并可以在编译时检查集合中 元素的类型,如果试图想集合中添加不满足类型要求的对象,编译器就会提示错误.增加泛型的集合,可以让代码更加简洁,程序更加健壮(java泛型可以保证如果程序在编译时候没有警告,运行时就不会产生ClassCastException). ③如果不设计泛型,那么集合中就可以装任何类型,所有可能引起异常。由于把对象丢进 集合时,集合失去了对象的状态信息,集合只知道它装的是Object类型元素,因此不仅麻烦还容易引起异常。 泛型 所谓泛型,就是允许在定义类、接口时指定类型形参,这个类型参数将在申明变量。创建对象时确定(既传入实际的类型参数,也可称为类型参数)。 代码欣赏:

上面三个接口定义的比较简单,除了尖括号中的内容—这就是泛型的实质:允许在定义接口、类时指定类型形参。类型形参在整个接口。类体内中可以当成类型使用,几乎所有可以使用其他普通类型的地方都可以使用这种类型参数。 泛型到底是什么?: 上面程序定义了一个带泛型声明的Apple类(先不要理会这个类型参数是否具有实际意义),实际使用Apple类的使用,必须给T传入实际类型。这样就生成了如Apple,Apple…等形式的逻辑子类(逻辑上不存在的子类)。 怎么使用泛型? 在开放中当我们需要一个父类,而父类中的方法或字段需要用到子类类型但是并不能确定到底要使用那一个子类类型或者遇到不知道子类具体类型,或者子类太多,不能写死一种子类时既可以给父类定义泛型,当实例化父类对象(或者使用父类对象创建子类对象时—多态)时再传入指定子类类型。

16JAVA第六单元练习题 泛型与集合

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) 2 泛型类跟接口及泛型方法 (3) 2.1 泛型类跟接口及继承 (3) 2.1.1泛型类 (3) 2.1.2继承 (3) 2.1.3接口 (3) 2.2 泛型方法 (3) 2.2.1 方法 (3) 2.2.2 类型推断 (4) 3 泛型实现原理 (5) 4 泛型数组 (6) 5边界 (7) 6通配符 (8) 7 泛型的问题及建议 (9) 7.1问题 (9) 7.2 建议 (9)

1 什么是泛型 从jdk1.5开始,Java中开始支持泛型了。泛型是一个很有用的编程工具,给我们带来了极大的灵活性。在看了《java核心编程》之后,我小有收获,写出来与大家分享。 所谓泛型,我的感觉就是,不用考虑对象的具体类型,就可以对对象进行一定的操作,对任何对象都能进行同样的操作。这就是灵活性之所在。但是,正是因为没有考虑对象的具体类型,因此一般情况下不可以使用对象自带的接口函数,因为不同的对象所携带的接口函数不一样,你使用了对象A的接口函数,万一别人将一个对象B传给泛型,那么程序就会出现错误,这就是泛型的局限性。所以说,泛型的最佳用途,就是用于实现容器类,实现一个通用的容器。该容器可以存储对象,也可以取出对象,而不用考虑对象的具体类型。因此,在学习泛型的时候,一定要了解这一点,你不能指望泛型是万能的,要充分考虑到泛型的局限性。下面我们来探讨一下泛型的原理以及高级应用。首先给出一个泛型类: public class Pair { public Pair() { first = null; second = null; } public Pair(T first, T second) { this.first = first; this.second = second; } public T getFirst() { return first; } public T getSecond() { return second; } public void setFirst(T newValue) { first = newValue; } public void setSecond(T newValue) { second = newValue; } private T first; private T second; } 我们看到,上述Pair类是一个容器类(我会多次强调,泛型天生就是为了容器类的方便实现),容纳了2个数据,但这2个数据类型是不确定的,用泛型T来表示。关于泛型类如何使用,那是最基本的内容,在此就不讨论了。

实验七: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泛型详解

Java 泛型详解 泛型是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()); }}public class Pair { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public void setKey(K key) { this.key = key; } public void setValue(V value) { this.value = value; } public K getKey() { return key; } public V getValue() { return value; }} 我们可以像下面这样去调用泛型方法: Pair p1 = new Pair(1, 'apple');Pair p2 = new Pair(2, 'pear');boolean same = https://www.wendangku.net/doc/0b10376729.html,pare(p1, p2); 或者在Java1.7/1.8利用type inference,让Java自动推导出相应的类型参数: Pair p1 = new Pair(1, 'apple');Pair p2 = new Pair(2, 'pear');boolean same = https://www.wendangku.net/doc/0b10376729.html,pare(p1, p2);

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

泛型可提高代码的高扩展性和重用率. 1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。 2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。 3、泛型的类型参数可以有多个。 4、泛型的参数类型可以使用extends语句,例如。习惯上称为“有界类型”。 5、泛型的参数类型还可以是通配符类型。例如Class classType = Class.forName("https://www.wendangku.net/doc/0b10376729.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;

java中的泛型

Java中的泛型 JDK1.5令我们期待很久,可是当他发布的时候却更换版本号为5.0。这说明Java已经有大幅度的变化。本文将讲解JDK5.0支持的新功能-----Java的泛型. 1、Java泛型 其实Java的泛型就是创建一个用类型作为参数的类。就象我们写类的方法一样,方法是这样的method(String str1,String str2 ),方法中参数str1、str2的值是可变的。而泛型也是一样的,这样写class Java_Generics<K,V>,这里边的K和V就象方法中的参数str1和str2,也是可变。下面看看例子: 正确输出:value 这只是个例子(Java中集合框架都泛型化了,这里费了2遍事.),不过看看是不是创建一个用类型作为参数的类,参数是K,V,传入的“值”是String类型。这个类他没有特定的待处理型别,以前我们定义好了一个类,在输入输入参数有所固定,是什么型别的有要求,但是现在编写程序,完全可以不制定参数的类型,具体用的时候来确定,增加了程序的通用性,像是一个模板。 呵呵,类似C++的模板(类似)。 1.1. 泛型通配符 下面我们先看看这些程序:

看看这个方法有没有异议,这个方法会通过编译的,假如你传入String,就是这样List <String>。 接着我们调用它,问题就出现了,我们将一个List<String>当作List传给了方法,JVM会给我们一个警告,说这个破坏了类型安全,因为从List中返回的都是Object类型的,而让我们再看看下面的方法。 因为这里的List<String>不是List<Object>的子类,不是String与Object的关系,就是说List<String>不隶属于list<Object>,他们不是继承关系,所以是不行的,这里的extends是表示限制的。 类型通配符是很神奇的,List<?>这个你能为他做什么呢?怎么都是“?”,它似乎不确定,他总不能返回一个?作为类型的数据吧,是啊他是不会返回一个“?”来问程序员的?JVM会做简单的思考的,看看代码吧,更直观些。 这段代码没问题的,l1.get(0)将返回一个Object。 1.2. 编写泛型类要注意: 1) 在定义一个泛型类的时候,在“<>”之间定义形式类型参数,例如:“class TestGen <K,V>”,其中“K” , “V”不代表值,而是表示类型。 2) 实例化泛型对象的时候,一定要在类名后面指定类型参数的值(类型),一共要有两次书写。例如: TestGen<String,String> t=new TestGen<String,String>();

C++模板函数与Java泛型

C++模板函数 #include "iostream" using namespace std; intint_ab(inta,int b) { returna+a*b; } doubledouble_ab(double a,double b) { returna+a*b; } int main(){ cout<或者template来定义一个任意类型的数据T,定义后的函数必须紧跟着。 #include "iostream" using namespace std; template T1 ab(T1 a,T1 b) { returna+a*b; } int main(){ cout< T1 ab(T1 a,T1 b)

8. Java基础_泛型

泛型 1. 介绍 下面是那种典型用法: List myIntList = new ArrayList();// 1 myIntList.add(new Integer(0));// 2 Integer x = (Integer) myIntList.iterator().next();// 3 第3 行的类型转换有些烦人。通常情况下,程序员知道一个特定的list 里边放的是什么类型的数据。但是,这个类型转换是必须的(essential)。编 译器只能保证iterator 返回的是Object 类型。为了保证对Integer 类型变量赋值的类型安全,必须进行类型转换。 当然,这个类型转换不仅仅带来了混乱,它还可能产生一个运行时错误(run time error),因为程序员可能会犯错。 程序员如何才能明确表示他们的意图,把一个list(集合) 中的内容限制 为一个特定的数据类型呢?这就是generics 背后的核心思想。这是上面程序片断的一个泛型版本: List myIntList = new ArrayList(); // 1 myIntList.add(new Integer(0)); // 2 Integer x = myIntList.iterator().next(); // 3 注意变量myIntList 的类型声明。它指定这不是一个任意的List,而是 一个Integer 的List,写作:List。我们说List 是一个带一个类型参数的泛型接口(a generic interface that takes a type parameter),本 例中,类型参数是Integer。我们在创建这个List 对象的时候也指定了一个类型参数。 另一个需要注意的是第 3 行没了类型转换。 现在,你可能认为我们已经成功地去掉了程序里的混乱。我们用第1 行的类型参数取代了第 3 行的类型转换。然而,这里还有个很大的不同。编译器现在能够在编译时检查程序的正确性。当我们说myIntList 被声明为List类型,这告诉我们无论何时何地使用myIntList 变量,编译器保证其中的元素的正确的类型。 实际结果是,这可以增加可读性和稳定性(robustness),尤其在大型的 程序中。 2. 定义简单的泛型 下面是从java.util包中的List接口和Iterator 接口的定义中摘录的片断:public interface List { void add(E x); Iterator iterator(); } public interface Iterator {

Java泛型使用详细分析

Java 泛型使用详细分析 、泛型的简介 1、为什么要使用泛型? 一般使用在集合上,比如现在把一个字符串类型的值放入到集合里面,这个时候,这个值放到集合之后,失去本身的类型,只能是object 类型。这时,如果想要对这个值进行类型转换,很容易出现类型转换错误,怎么解决这个问题,可以使用泛型来解决。 2、在泛型里面写是一个对象,String 不能写基本的数据类型比如int, 要写基本的数据类型对应的包装类 、在集合上如何使用泛型 - 常用集合list set map - 泛型语法:集合 比如list // 泛型在list 上的使用 @Test

public void testList() { List list = new ArrayList(); list.add("aaa"); list.add("bbb"); list.add("ccc"); //for 循环 for (int i = 1;i it = list.iterator(); while (it.hasNext()){ System.out.println(it.next()); } // 泛型在set 上的使用 @Test public void testSet() {

Java泛型实现单链表

学号11710115 天津城建大学 Java 语言程序设计C 实验报告 实验3:泛型实现链表 学生姓名路江飞 班级11卓越七班

一、实验内容 1.掌握使用Java语言进行结构化程序设计; 2.熟悉Java泛型。 3.熟悉Eclipse开发环境,编写简单的Application程序,并编译和执行。 二、实验要求 1.调试程序、编译,运行后得到正确的结果; 2.写出实验报告,要求记录编译和执行Java程序当中的系统错误信息提示,并给出解决办法。 三、实验结果 文件1: package _List; class Node{ E e; Node next; Node(E e){ this.e=e; next=null; } } 文件2: package _List; public class Show { void print(){ System.out.println("*************************************"); System.out.println("* 1.按位查找*"); System.out.println("* 2.按值查找*"); System.out.println("* 3.插入*"); System.out.println("* 4.按位删除*"); System.out.println("* 5.按值删除*"); System.out.println("* 6.修改*"); System.out.println("* 7.遍历*");

System.out.println("* 8.查看链表长度*"); System.out.println("* 9.退出*"); System.out.println("*************************************"); } } 文件3: package _List; import java.util.Scanner; public class List { Node head; List(){ this.head=null; } List(E e[],int n){ //构造函数 this.head=new Node (e[0]); Node r=this.head; for(int i=1;is=new Node (e[i]); r.next=s; r=s; } r.next=null; } void add (E e){//按位置插入 int index,b=1; Scanner input=new Scanner(System.in); for(int j=0;b==1;j++){ System.out.print("请输入插入的位置(第一个数据之前是0号位置,以此类推):");

Java泛型的规则和限制

Java泛型的规则和限制 在学习编程的过程中,我觉得不止要获得课本的知识, 更多的是通过学习技术知识提高解决问题的能力,这样我们才能走在最前方,更多Java学习,请登陆疯狂java官网。 我们在学习Java的过程中,对Java泛型的理解至关重要。Java中的泛型的本质是参数化类型。也就是说,所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。安全、简单是Java引入泛型的好处 Java工程师介绍,在Java SE 1.5之前,没有泛型的情况的下,通过对类型ObJect的引用来实现参数的“任意化” “任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。 泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。 泛型在使用中的规则和限制 1、泛型的类型参数可以有多个。 2、泛型的参数类型可以使用extends语句,例如。习惯上成为“有界类型”。 3、泛型的参数类型还可以是通配符类型。例如Class classType = Class.forName(https://www.wendangku.net/doc/0b10376729.html,ng.String); 4、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。 5、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。 疯狂Java培训专注软件开发培训,提升学员就业能力,重点提升实践动手能力。技术知识沉淀深厚的老师,让你感受Java的魅力,激发你对于编程的热爱,让你在半年的时间内掌握8-10万的代码量,掌握Java核心技术,成为真正的技术高手;通过大量全真企业项目疯狂训练,迅速积累项目经验。让你成为技能型的现代化高端人才,迅速获得高薪就业!时间不等人,赶紧联系我们吧!

2019年10道关于Java泛型的面试题-word范文 (4页)

本文部分内容来自网络整理,本司不为其真实性负责,如有异议或侵权请及时联系,本司将立即删除! == 本文为word格式,下载后可方便编辑和修改! == 10道关于Java泛型的面试题 1. Java中的泛型是什么 ? 使用泛型的好处是什么? 这是在各种Java泛型面试中,一开场你就会被问到的问题中的一个,主要集中在初级和中级面试中。那些拥有Java1.4或更早版本的开发背景的人都知道,在集合中存储对象并在使用前进行类型转换是多么的不方便。泛型防止了那种 情况的发生。它提供了编译期的类型安全,确保你只能把正确类型的对象放入 集合中,避免了在运行时出现ClassCastException。 2. Java的泛型是如何工作的 ? 什么是类型擦除 ? 这是一道更好的泛型面试题。泛型是通过类型擦除来实现的,编译器在编译时 擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如List在运行时仅用一个List来表示。这样做的目的,是确保能和 Java 5之前的版本开发二进制类库进行兼容。你无法在运行时访问到类型参数,因为编译器已经把泛型类型转换成了原始类型。根据你对这个泛型问题的回答 情况,你会得到一些后续提问,比如为什么泛型是由类型擦除来实现的或者给你展示一些会导致编译器出错的错误泛型代码。请阅读我的Java中泛型是如何工作的来了解更多信息。 3. 什么是泛型中的限定通配符和非限定通配符 ? 这是另一个非常流行的Java泛型面试题。限定通配符对类型进行了限制。有两种限定通配符,一种是它通过确保类型必须是T的子类来设定类型的上界,另一种是它通过确保类型必须是T的父类来设定类型的下界。泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。另 一方面表示了非限定通配符,因为可以用任意类型来替代。更多信息请参阅我的文章泛型中限定通配符和非限定通配符之间的区别。 4. List和List 之间有什么区别 ? 这和上一个面试题有联系,有时面试官会用这个问题来评估你对泛型的理解, 而不是直接问你什么是限定通配符和非限定通配符。这两个List的声明都是 限定通配符的例子,List可以接受任何继承自T的类型的List,而List可以接受任何T的父类构成的List。例如List

java 集合与泛型

Collection 接口及实现类 Java 语言的Collection 接口及实现类是在java.util 包中定义的,其中定义了多个接口和类,它们统称为Java 集合框架(Java Collection Framework )。 Java 集合框架由两种类型构成,一个是Collection ,另一个是Map 。Collection 对象用于存放一组对象,Map 对象用于存放一组关键字/值的对象。Collection 和Map 是最基本的接口,它们又有子接口,这些接口的层次关系如图1所示。 图1 Java 集合框架的接口继承关系 1.1 Collection 接口及操作 Collection 接口是所有集合类型的根接口,它有三个子接口:Set 接口、List 接口和Queue 接口。 Collection 接口的定义如下: public interface Collection extends Iterable { // 基本操作 int size(); boolean isEmpty(); boolean contains(Object element); boolean add(E element); boolean remove(Object element); Iterator iterator(); // 批量操作 boolean containsAll(Collection c); boolean addAll(Collection c); boolean removeAll(Collection c); boolean retainAll(Collection c); void clear(); // 数组操作 Object[] toArray(); T[] toArray(T[] a); } 说明 从JDK 1.5开始,Java 开始支持范型(generics )的概念。在Collection 接口 的声明中,就表示该接口支持范型,它指的是集合中的对象类型。这样,当我们声明一个Collection 实例时,应该使用这种方式指明包含在集合中的对象类型。这可以使编译器在编译时检查存入集合的对象类型是否正确,从而减少运行时错误。 Collection 接口中定义的方法主要包括三类:集合操作、批量操作和数组操作。