Java泛型extends关键字设置边界的实现
本文主要介绍在泛型定义中的<>中的占位符如何配合extends关键字使用,形如
- 一般的泛型定义中的
,相当于 ,而类型擦除则会将类型参数擦除成T的上界,即Object。则在泛型定义中作为T类型的对象可以调用Object的函数和属性。 - 使用了extends的泛型定义中的
,其上界已被明显定义了,此时会将类型参数擦除成Integer。则在泛型定义中作为T类型的对象可以调用Integer的函数和属性。
接下来本文将以几个示例和具体分析来讲解剩下的知识点。
类型参数多边界的分析
此例中的泛型类,类型参数带有多个边界。讲下类的实际意义:Dimension代表物体的方位、HasColor代表物体的颜色、Weight代表物体的重量。
interfaceHasColor{java.awt.ColorgetColor();} classColored{ Titem; Colored(Titem){this.item=item;} TgetItem(){returnitem;} //Theboundallowsyoutocallamethod: java.awt.Colorcolor(){returnitem.getColor();} } classDimension{publicintx,y,z;} //Thiswon'twork--classmustbefirst,theninterfaces: //classColoredDimension {} //Multiplebounds: classColoredDimension { Titem; ColoredDimension(Titem){this.item=item;} TgetItem(){returnitem;} java.awt.Colorcolor(){returnitem.getColor();} intgetX(){returnitem.x;} intgetY(){returnitem.y;} intgetZ(){returnitem.z;} } interfaceWeight{intweight();} //Aswithinheritance,youcanhaveonlyone //concreteclassbutmultipleinterfaces: classSolid { Titem; Solid(Titem){this.item=item;} TgetItem(){returnitem;} java.awt.Colorcolor(){returnitem.getColor();} intgetX(){returnitem.x;} intgetY(){returnitem.y;} intgetZ(){returnitem.z;} intweight(){returnitem.weight();} } classBoundedextendsDimensionimplementsHasColor,Weight{ publicjava.awt.ColorgetColor(){returnnull;} publicintweight(){return0;} } publicclassBasicBounds{ publicstaticvoidmain(String[]args){ Solid solid=newSolid (newBounded()); solid.color(); solid.getY(); solid.weight(); } }///:~
- classColored
这个泛型类的泛型定义中,要求了类型参数T的边界为HasColor,正因如此,在函数java.awt.Colorcolor(){returnitem.getColor();}中便可以通过一个T类型的变量item来调用属于HasColor的方法。 - classColoredDimension
{},此时定义了边界同时为HasColor&Dimension,但是由于编译器要求占位符后的extends后第一个必须是类,之后的必须是接口(这就和正常的类的继承规则一样),所以此句通不过编译。而classColoredDimension 给出了正确的定义,即第一个必须是类,之后的必须是接口。 - classColoredDimension
的类定义中,因为T的边界是HasColor&Dimension,所以在类定义中,既可以获取Dimension的属性,也可以调用HasColor的方法。 - classSolid
的类定义中,extends后第一个是类,之后的都是接口,符合刚才讲的规则。同理,也可以从这些边界中,获取属性,调用方法。 - classBoundedextendsDimensionimplementsHasColor,Weight这个类将在生成泛型类对象,用来指定具体类型为Bounded。因为classSolid
的类型参数T的要求是extendsDimension&HasColor&Weight,所以指定具体类型为newSolid ,是可以的。因为类定义中构造器的声明为Solid(Titem),且具体类型为newSolid 中指定的Bounded,所以要求构造器的实参为Bounded或者Bounded的子类。
classderivedBoundedextendsBounded{} classBounded1extendsDimensionimplementsHasColor,Weight{ publicjava.awt.ColorgetColor(){returnnull;} publicintweight(){return0;} } publicclassBasicBounds{ publicstaticvoidmain(String[]args){ //Solidsolid=newSolid (newderivedBounded());//给定的具体类型不符合边界 Solid solid1=newSolid (newderivedBounded());//可以传递具体类型Bounded的子类 //Solid solid2=newSolid (newBounded1());//编译报错,因为泛型的静态类型检查 solid1.color(); solid1.getY(); solid1.weight(); } }///:~
根据上一条,那么newSolid
但是类型参数有多个边界时,java内部即java字节码到底是怎么处理的呢:
publicstaticvoidmain(java.lang.String[]); Code: 0:new#2//classSolid 3:dup 4:new#3//classBounded 7:dup 8:invokespecial#4//MethodBounded."":()V 11:invokespecial#5//MethodSolid." ":(LDimension;)V 14:astore_1
从MethodSolid."
//Sourcecoderecreatedfroma.classfilebyIntelliJIDEA //(poweredbyFernflowerdecompiler) importjava.awt.Color; classSolid{ Titem; Solid(Titem){ this.item=item; } TgetItem(){ returnthis.item; } Colorcolor(){ return((HasColor)this.item).getColor();//类型转换为其他边界,再调用方法 } intgetX(){ returnthis.item.x; } intgetY(){ returnthis.item.y; } intgetZ(){ returnthis.item.z; } intweight(){ return((Weight)this.item).weight();//类型转换为其他边界,再调用方法 } }
当调用的方法不属于第一个边界时,就进行类型转换处理为其他边界就行,反正T肯定是符合extendsDimension&HasColor&Weight的。
继承有边界要求的泛型类
通过观察上例可知,Colored、ColoredDimension、Solid这三个类在持有对象的方面有冗余的地方:都有同一个成员变量、同一个构造器、同一个get函数。而类型参数上,边界也是依次叠加的。同样,对于这些边界所带来的属性和方法,也是冗余的。
所以下例对其进行了修改,通过继承来消除冗余,注意,下面继承的泛型类对类型参数是有边界要求的:
//HoldItem对边界T没有要求 classHoldItem{ Titem; HoldItem(Titem){this.item=item;} TgetItem(){returnitem;} } //Colored2对边界T有HasColor的要求 classColored2 extendsHoldItem { Colored2(Titem){super(item);} java.awt.Colorcolor(){returnitem.getColor();} } //ColoredDimension2对边界T有Dimension&HasColor的要求 classColoredDimension2 extendsColored2 { ColoredDimension2(Titem){super(item);} intgetX(){returnitem.x;} intgetY(){returnitem.y;} intgetZ(){returnitem.z;} } //Solid2对边界T有Dimension&HasColor&Weight的要求,不过没有类继承它了 classSolid2 extendsColoredDimension2 { Solid2(Titem){super(item);} intweight(){returnitem.weight();} } publicclassInheritBounds{ publicstaticvoidmain(String[]args){ Solid2 solid2= newSolid2 (newBounded()); solid2.color(); solid2.getY(); solid2.weight(); } }///:~
- HoldItem这个泛型类通过类型参数T把成员变量、构造器、get函数都定义好了,之后的类通过继承它就可以获得这些属性和方法。
- Colored2泛型类继承了HoldItem,获得了后者的属性和方法从而减少了冗余。同时,classColored2
属于“定义泛型类”,extendsHoldItem 属于“使用泛型类”,使用泛型类需要指定具体类型,现在确定具体类型的任务延后到了 的确认。再从边界是否符合的情况分析,HoldItem的要求是 属于无边界, 这样的边界定义属于HasColor边界,从范围上说 是小于等于 的,这样是可以的。由于T添加了HasColor边界,所以可以调用item.getColor()方法了。 - ColoredDimension2泛型类继承了Colored2。从边界是否符合的情况分析,Colored2对T的边界要求是
,而ColoredDimension2定义中的T的边界是 , 小于等于 ,符合要求。换句话说,ColoredDimension2定义中的T的边界必须比Colored2的边界要求一致,或者范围更小。 - Solid2泛型类继承了ColoredDimension2。从边界是否符合的情况分析,
小于等于ColoredDimension2的边界要求 ,符合要求。
总结一下:
当一个泛型类继承另一个泛型类时(前者属于“定义泛型类”,后者属于“使用泛型类”),且使用了同一个类型参数时,定义泛型类的类型参数边界定义一定要小于等于使用的那个泛型类的边界要求。
泛型方法中的边界定义
泛型方法中对类型参数的边界定义,同样也得符合使用的泛型类的边界要求。此例中,泛型类同样继承别的泛型类,分析同上不赘述。讲下类的实际意义:一系列接口代表了超能力、一系列类代表了超级英雄,它们拥有一个超能力的成员变量。
importjava.util.*; interfaceSuperPower{} interfaceXRayVisionextendsSuperPower{ voidseeThroughWalls(); } interfaceSuperHearingextendsSuperPower{ voidhearSubtleNoises(); } interfaceSuperSmellextendsSuperPower{ voidtrackBySmell(); } classSuperHero{ POWERpower; SuperHero(POWERpower){this.power=power;} POWERgetPower(){returnpower;} } classSuperSleuth extendsSuperHero { SuperSleuth(POWERpower){super(power);} voidsee(){power.seeThroughWalls();} } classCanineHero extendsSuperHero { CanineHero(POWERpower){super(power);} voidhear(){power.hearSubtleNoises();} voidsmell(){power.trackBySmell();} } classSuperHearSmellimplementsSuperHearing,SuperSmell{ publicvoidhearSubtleNoises(){} publicvoidtrackBySmell(){} } classDogBoyextendsCanineHero { DogBoy(){super(newSuperHearSmell());} } publicclassEpicBattle{ //Boundsingenericmethods: static voiduseSuperHearing(SuperHero hero){//泛型方法 hero.getPower().hearSubtleNoises(); } static voidsuperFind(SuperHero hero){//泛型方法 hero.getPower().hearSubtleNoises(); hero.getPower().trackBySmell(); } publicstaticvoidmain(String[]args){ DogBoydogBoy=newDogBoy(); useSuperHearing(dogBoy); superFind(dogBoy); //Youcandothis: ListaudioBoys; //Butyoucan'tdothis: //ListdogBoys; } }///:~
- 主函数中的useSuperHearing泛型方法中,其对T的边界定义为
。而在形参中使用了泛型类SuperHero ,其对边界的要求是 。因为SuperHearing继承了SuperPower,所以边界定义符合了对边界的要求。 - 主函数中的useSuperHearing泛型方法中,其对T的边界定义为
。正因如此,在方法中便可以调用SuperHearing的方法hearSubtleNoises了。
其他
本文例子均来自java编程思想,例子本身不错,但奈何作者对其做的讲解很少,所以本人为其加上了详细的分析。其实这些例子都需要反复琢磨,精读之后才会对泛型的extends关键字有深刻的理解。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。