本文主要介绍在泛型定义中的< >中的占位符如何配合extends关键字使用,形如<T extends Integer>。泛型定义存在于这三种形式中:泛型类、泛型接口、泛型方法。
接下来本文将以几个示例和具体分析来讲解剩下的知识点。
类型参数多边界的分析
此例中的泛型类,类型参数带有多个边界。讲下类的实际意义:Dimension代表物体的方位、HasColor代表物体的颜色、Weight代表物体的重量。
interface HasColor { java.awt.Color getColor(); } class Colored<T extends HasColor> { T item; Colored(T item) { this.item = item; } T getItem() { return item; } // The bound allows you to call a method: java.awt.Color color() { return item.getColor(); } } class Dimension { public int x, y, z; } // This won't work -- class must be first, then interfaces: // class ColoredDimension<T extends HasColor & Dimension> { } // Multiple bounds: class ColoredDimension<T extends Dimension & HasColor> { T item; ColoredDimension(T item) { this.item = item; } T getItem() { return item; } java.awt.Color color() { return item.getColor(); } int getX() { return item.x; } int getY() { return item.y; } int getZ() { return item.z; } } interface Weight { int weight(); } // As with inheritance, you can have only one // concrete class but multiple interfaces: class Solid<T extends Dimension & HasColor & Weight> { T item; Solid(T item) { this.item = item; } T getItem() { return item; } java.awt.Color color() { return item.getColor(); } int getX() { return item.x; } int getY() { return item.y; } int getZ() { return item.z; } int weight() { return item.weight(); } } class Bounded extends Dimension implements HasColor, Weight { public java.awt.Color getColor() { return null; } public int weight() { return 0; } } public class BasicBounds { public static void main(String[] args) { Solid<Bounded> solid = new Solid<Bounded>(new Bounded()); solid.color(); solid.getY(); solid.weight(); } } ///:~
class derivedBounded extends Bounded {} class Bounded1 extends Dimension implements HasColor, Weight { public java.awt.Color getColor() { return null; } public int weight() { return 0; } } public class BasicBounds { public static void main(String[] args) { //Solid<Bounded> solid = new Solid<Integer>(new derivedBounded());//给定的具体类型不符合边界 Solid<Bounded> solid1 = new Solid<Bounded>(new derivedBounded());//可以传递具体类型Bounded的子类 //Solid<Bounded> solid2 = new Solid<Bounded>(new Bounded1());//编译报错,因为泛型的静态类型检查 solid1.color(); solid1.getY(); solid1.weight(); } } ///:~
根据上一条,那么new Solid<Integer>(new Bounded())这里指定的具体类型,由于和泛型类定义的T类型参数的要求extends Dimension & HasColor & Weight不相符,所以编译会报错;给构造器传值时,实参可以是Bounded的子类;一个同样继承了相同边界的类Bounded1 ,不能传递给构造器,因为类型已经被指定为Bounded了。
但是类型参数有多个边界时,java内部即java字节码到底是怎么处理的呢:
public static void main(java.lang.String[]); Code: 0: new #2 // class Solid 3: dup 4: new #3 // class Bounded 7: dup 8: invokespecial #4 // Method Bounded."<init>":()V 11: invokespecial #5 // Method Solid."<init>":(LDimension;)V 14: astore_1
从Method Solid."<init>":(LDimension;)V可以看到,给Solid的构造器传递参数时,编译器认为这个形参是个Dimension,这就是编译器处理多个边界的方法,永远处理为第一个边界,即类型擦除为第一个边界。但剩下的两个边界怎么办呢,这里都被处理第一个边界了,我们再去看一下Solid.class的反编译代码就能找到答案:
// Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) import java.awt.Color; class Solid<T extends Dimension & HasColor & Weight> { T item; Solid(T item) { this.item = item; } T getItem() { return this.item; } Color color() { return ((HasColor)this.item).getColor();//类型转换为其他边界,再调用方法 } int getX() { return this.item.x; } int getY() { return this.item.y; } int getZ() { return this.item.z; } int weight() { return ((Weight)this.item).weight();//类型转换为其他边界,再调用方法 } }
当调用的方法不属于第一个边界时,就进行类型转换处理为其他边界就行,反正T肯定是符合extends Dimension & HasColor & Weight的。
继承有边界要求的泛型类
通过观察上例可知,Colored、ColoredDimension、Solid这三个类在持有对象的方面有冗余的地方:都有同一个成员变量、同一个构造器、同一个get函数。而类型参数上,边界也是依次叠加的。同样,对于这些边界所带来的属性和方法,也是冗余的。
所以下例对其进行了修改,通过继承来消除冗余,注意,下面继承的泛型类对类型参数是有边界要求的:
//HoldItem对边界T没有要求 class HoldItem<T> { T item; HoldItem(T item) { this.item = item; } T getItem() { return item; } } //Colored2对边界T有HasColor的要求 class Colored2<T extends HasColor> extends HoldItem<T> { Colored2(T item) { super(item); } java.awt.Color color() { return item.getColor(); } } //ColoredDimension2对边界T有Dimension & HasColor的要求 class ColoredDimension2<T extends Dimension & HasColor> extends Colored2<T> { ColoredDimension2(T item) { super(item); } int getX() { return item.x; } int getY() { return item.y; } int getZ() { return item.z; } } //Solid2对边界T有Dimension & HasColor & Weight的要求,不过没有类继承它了 class Solid2<T extends Dimension & HasColor & Weight> extends ColoredDimension2<T> { Solid2(T item) { super(item); } int weight() { return item.weight(); } } public class InheritBounds { public static void main(String[] args) { Solid2<Bounded> solid2 = new Solid2<Bounded>(new Bounded()); solid2.color(); solid2.getY(); solid2.weight(); } } ///:~
总结一下:
当一个泛型类继承另一个泛型类时(前者属于“定义泛型类”,后者属于“使用泛型类”),且使用了同一个类型参数时,定义泛型类的类型参数边界定义一定要小于等于使用的那个泛型类的边界要求。
泛型方法中的边界定义
泛型方法中对类型参数的边界定义,同样也得符合使用的泛型类的边界要求。此例中,泛型类同样继承别的泛型类,分析同上不赘述。讲下类的实际意义:一系列接口代表了超能力、一系列类代表了超级英雄,它们拥有一个超能力的成员变量。
import java.util.*; interface SuperPower {} interface XRayVision extends SuperPower { void seeThroughWalls(); } interface SuperHearing extends SuperPower { void hearSubtleNoises(); } interface SuperSmell extends SuperPower { void trackBySmell(); } class SuperHero<POWER extends SuperPower> { POWER power; SuperHero(POWER power) { this.power = power; } POWER getPower() { return power; } } class SuperSleuth<POWER extends XRayVision> extends SuperHero<POWER> { SuperSleuth(POWER power) { super(power); } void see() { power.seeThroughWalls(); } } class CanineHero<POWER extends SuperHearing & SuperSmell> extends SuperHero<POWER> { CanineHero(POWER power) { super(power); } void hear() { power.hearSubtleNoises(); } void smell() { power.trackBySmell(); } } class SuperHearSmell implements SuperHearing, SuperSmell { public void hearSubtleNoises() {} public void trackBySmell() {} } class DogBoy extends CanineHero<SuperHearSmell> { DogBoy() { super(new SuperHearSmell()); } } public class EpicBattle { // Bounds in generic methods: static <POWER extends SuperHearing> void useSuperHearing(SuperHero<POWER> hero) {//泛型方法 hero.getPower().hearSubtleNoises(); } static <POWER extends SuperHearing & SuperSmell> void superFind(SuperHero<POWER> hero) {//泛型方法 hero.getPower().hearSubtleNoises(); hero.getPower().trackBySmell(); } public static void main(String[] args) { DogBoy dogBoy = new DogBoy(); useSuperHearing(dogBoy); superFind(dogBoy); // You can do this: List<? extends SuperHearing> audioBoys; // But you can't do this: // List<? extends SuperHearing & SuperSmell> dogBoys; } } ///:~
其他
本文例子均来自java编程思想,例子本身不错,但奈何作者对其做的讲解很少,所以本人为其加上了详细的分析。其实这些例子都需要反复琢磨,精读之后才会对泛型的extends关键字有深刻的理解。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
问题内容: 以下两个签名是否相同? 和 问题答案: 不,两个签名不相同。根据Java语言规范的第8章: 如果两个方法具有相同的名称和参数类型,则它们具有相同的签名。 如果满足以下所有条件,则两个方法或构造函数声明M和N具有相同的参数类型: 它们具有相同数量的形式参数(可能为零) 它们具有相同数量的类型参数 (可能为零) … 由于两种方法共享的类型参数数量不同,因此签名也不相同。 在实际情况下,使用
问题内容: 但是,我似乎仍然对super关键字感到迷茫: 当我们声明这样的集合时: 不应该相反吗?我们有一个列表,其中包含一些对象(类型未知),它们是的父对象Number。因此Object应该适合(因为它是的父项),而不应该。由于某种原因,情况恰恰相反。 只要我们有以下代码 编译上面的代码是不可能的(我的理智建议这是正确的行为),但是基本逻辑可以证明是相反的: 我知道这很疯狂,但这不是他们不允许使
问题内容: 为什么我只能使用通配符而不使用类型参数? 例如,在界面中,为什么方法不是这样写的 问题答案: super绑定命名类型参数(例如)而不是通配符(例如)是非法的,这仅仅是因为即使允许,它也不会执行你希望的操作,因为既然这是所有引用类型的最终值,一切是,实际上有没有约束。 在你的特定示例中,由于任何引用类型的数组都是(通过Java数组协方差),因此可以在编译时用作(如果这样的绑定是合法的)自
现在我们已经是泛型专家了,为什么不扩展LongPreference为支持所有Shared Preferences支持的类型呢?我们来创建一个Preference委托: class Preference<T>(val context: Context, val name: String, val default: T) : ReadWriteProperty<Any?, T> { v
本文向大家介绍Java继承extends与super关键字详解,包括了Java继承extends与super关键字详解的使用技巧和注意事项,需要的朋友参考一下 函数也称为方法! 继承:在java中使用extends关键字来表示继承关系。super用来继承父类方法和参数。 继承就是子类继承父类的特征和行为,使得子类具有父类相同的行为。 注意事项: 1、当一个类没有继承任何一个类时,系统默认继承Obj
编译以下Java代码: 此外,编译以下代码: 以下代码将给我一个编译错误: 因此,看起来,如果其中一个边界类型本身是类型参数,就不可能有多个边界。但为什么呢? 据我所知,如果声明了多个边界,那么最多一个边界可能是一个类(其他所有边界都是接口),并且它必须是第一个边界。 但是,既然在我的示例中是一个接口,为什么编译器会抱怨呢?毕竟,T是接口还是类应该没有任何区别。 我试图在网上找到答案,但显然我的谷