这是对泛型的介绍性讨论的延续, 此处的先前部分可以在此处找到。

在上一篇文章中,我们讨论了关于类型参数的递归边界。 我们看到了递归绑定如何帮助我们重用了车辆比较逻辑。 在该文章的结尾,我建议当我们不够小心时,可能会发生类型混合。 今天我们将看到一个例子。

如果有人错误地通过以下方式创建了Vehicle的子类,则可能会发生混合:

/*** Definition of Vehicle*/
public abstract class Vehicle<E extends Vehicle<E>> implements Comparable<E> {// other methods and propertiespublic int compareTo(E vehicle) {// method implementation}
}/*** Definition of Bus*/
public class Bus extends Vehicle<Bus> {}/*** BiCycle, new subtype of Vehicle*/
public class BiCycle extends Vehicle<Bus> {}/*** Now this class’s compareTo method will take a Bus type* as its argument. As a result, you will not be able to compare* a BiCycle with another Bicycle, but with a Bus.*/
cycle.compareTo(anotherCycle);  // This will generate a compile time error
cycle.compareTo(bus);    // but you will be able to do this without any error

枚举不会发生这种类型的混淆,因为JVM负责子类化和为枚举类型创建实例,但是如果我们在代码中使用此样式,则必须小心。

让我们谈谈递归边界的另一个有趣的应用。 考虑以下类别:

public class MyClass {private String attrib1;private String attrib2;private String attrib3;private String attrib4;private String attrib5;public MyClass() {}public String getAttrib1() {return attrib1;}public void setAttrib1(String attrib1) {this.attrib1 = attrib1;}public String getAttrib2() {return attrib2;}public void setAttrib2(String attrib2) {this.attrib2 = attrib2;}public String getAttrib3() {return attrib3;}public void setAttrib3(String attrib3) {this.attrib3 = attrib3;}public String getAttrib4() {return attrib4;}public void setAttrib4(String attrib4) {this.attrib4 = attrib4;}public String getAttrib5() {return attrib5;}public void setAttrib5(String attrib5) {this.attrib5 = attrib5;}
}

如果我们要创建此类的实例,则可以执行以下操作:

MyClass mc = new MyClass();
mc.setAttrib1("Attribute 1");
mc.setAttrib2("Attribute 2");

上面的代码创建该类的实例并初始化属性。 如果我们可以在此处使用方法链接 ,那么我们可以编写:

MyClass mc = new MyClass().setAttrib1("Attribute 1").setAttrib2("Attribute 2");

显然比第一个版本好得多。 但是,要启用这种方法链接,我们需要通过以下方式修改MyClass

public class MyClass {private String attrib1;private String attrib2;private String attrib3;private String attrib4;private String attrib5;public MyClass() {}public String getAttrib1() {return attrib1;}public MyClass setAttrib1(String attrib1) {this.attrib1 = attrib1;return this;}public String getAttrib2() {return attrib2;}public MyClass setAttrib2(String attrib2) {this.attrib2 = attrib2;return this;}public String getAttrib3() {return attrib3;}public MyClass setAttrib3(String attrib3) {this.attrib3 = attrib3;return this;}public String getAttrib4() {return attrib4;}public MyClass setAttrib4(String attrib4) {this.attrib4 = attrib4;return this;}public String getAttrib5() {return attrib5;}public MyClass setAttrib5(String attrib5) {this.attrib5 = attrib5;return this;}
}

然后我们将可以对此类的实例使用方法链接。 但是,如果我们想在涉及继承的地方使用方法链接,那么事情就会变得混乱:

public abstract class Parent {private String attrib1;private String attrib2;private String attrib3;private String attrib4;private String attrib5;public Parent() {}public String getAttrib1() {return attrib1;}public Parent setAttrib1(String attrib1) {this.attrib1 = attrib1;return this;}public String getAttrib2() {return attrib2;}public Parent setAttrib2(String attrib2) {this.attrib2 = attrib2;return this;}public String getAttrib3() {return attrib3;}public Parent setAttrib3(String attrib3) {this.attrib3 = attrib3;return this;}public String getAttrib4() {return attrib4;}public Parent setAttrib4(String attrib4) {this.attrib4 = attrib4;return this;}public String getAttrib5() {return attrib5;}public Parent setAttrib5(String attrib5) {this.attrib5 = attrib5;return this;}
}public class Child extends Parent {private String attrib6;private String attrib7;public Child() {}public String getAttrib6() {return attrib6;}public Child setAttrib6(String attrib6) {this.attrib6 = attrib6;return this;}public String getAttrib7() {return attrib7;}public Child setAttrib7(String attrib7) {this.attrib7 = attrib7;return this;}
}/*** Now try using method chaining for instances of Child* in the following way, you will get compile time errors.*/
Child c = new Child().setAttrib1("Attribute 1").setAttrib6("Attribute 6");

这样做的原因是,即使Child从其父级继承了所有的setter,所有这些setter方法的返回类型也都是Parent类型,而不是Child类型。 因此,第一个设置器将返回类型为Parent的引用,调用setAttrib6会导致编译错误,因为它没有任何此类方法。

我们可以通过在Parent上引入通用类型参数并在其上定义递归绑定来解决此问题。 它的所有子项从其扩展时都将自己作为类型参数传递,从而确保setter方法将返回其类型的引用:

public abstract class Parent<T extends Parent<T>> {private String attrib1;private String attrib2;private String attrib3;private String attrib4;private String attrib5;public Parent() {}public String getAttrib1() {return attrib1;}@SuppressWarnings("unchecked")public T setAttrib1(String attrib1) {this.attrib1 = attrib1;return (T) this;}public String getAttrib2() {return attrib2;}@SuppressWarnings("unchecked")public T setAttrib2(String attrib2) {this.attrib2 = attrib2;return (T) this;}public String getAttrib3() {return attrib3;}@SuppressWarnings("unchecked")public T setAttrib3(String attrib3) {this.attrib3 = attrib3;return (T) this;}public String getAttrib4() {return attrib4;}@SuppressWarnings("unchecked")public T setAttrib4(String attrib4) {this.attrib4 = attrib4;return (T) this;}public String getAttrib5() {return attrib5;}@SuppressWarnings("unchecked")public T setAttrib5(String attrib5) {this.attrib5 = attrib5;return (T) this;}
}public class Child extends Parent<Child> {private String attrib6;private String attrib7;public String getAttrib6() {return attrib6;}public Child setAttrib6(String attrib6) {this.attrib6 = attrib6;return this;}public String getAttrib7() {return attrib7;}public Child setAttrib7(String attrib7) {this.attrib7 = attrib7;return this;}
}

请注意,我们已经明确地施放T类型,因为编译器不知道这种转换是否是可能的,即使它是因为牛逼的定义是由父<T>界。 同样,由于我们将对象引用转换为T ,因此编译器将发出未经检查的警告。 为了抑制这种情况,我们在设置器上方使用了@SuppressWarnings(“ unchecked”)

经过上述修改,这样做是完全有效的:

Child c = new Child().setAttrib1("Attribute 1").setAttrib6("Attribute 6");

当以这种方式编写方法设置器时,我们应注意不要将递归边界用于任何其他目的,例如从父级访问子级状态,因为这会使父级暴露其子类的内部细节,并最终破坏封装。

通过这篇文章,我完成了泛型的基本介绍。 我在本系列中没有讨论太多的事情,因为我认为它们已经超出了介绍的范围。

直到下一次。

翻译自: https://www.javacodegeeks.com/2014/07/an-introduction-to-generics-in-java-part-6.html

Java泛型简介–第6部分相关推荐

  1. 【中级05】Java泛型、反射

    java泛型简介 Java泛型(Generic)是J2 SE1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类 ...

  2. 【Java 泛型】泛型简介 ( 泛型类 | 泛型方法 | 静态方法的泛型 | 泛型类与泛型方法完整示例 )

    文章目录 一.泛型简介 二.泛型类 三.泛型方法 四.静态方法的泛型 五.泛型类与泛型方法完整示例 一.泛型简介 泛型 可以 简单理解为 参数化类型 , 主要作用在 类 , 方法 , 接口 上 ; j ...

  3. Java 里的泛型简介.

    我们在JDK中有时回见到1个类or接口后面跟这1个尖括号. 例如: java.util.HashMap<K,V> 我们一开始大概知道K,V 大概就是Key和Value的意思, 键值对嘛, ...

  4. java 泛型嵌套泛型_Java泛型简介–第6部分

    java 泛型嵌套泛型 这是关于泛型的介绍性讨论的延续, 此处的先前部分可以在此处找到. 在上一篇文章中,我们讨论了关于类型参数的递归边界. 我们看到了递归绑定如何帮助我们重用了车辆比较逻辑. 在该文 ...

  5. java泛型的泛型_Java 泛型总结(一):基本用法与类型擦除

    简介 Java 在 1.5 引入了泛型机制,泛型本质是参数化类型,也就是说变量的类型是一个参数,在使用时再指定为具体类型.泛型可以用于类.接口.方法,通过使用泛型可以使代码更简单.安全.然而 Java ...

  6. Java 泛型总结(三):通配符的使用

    简介 前两篇文章介绍了泛型的基本用法.类型擦除以及泛型数组.在泛型的使用中,还有个重要的东西叫通配符,本文介绍通配符的使用. 这个系列的另外两篇文章: Java 泛型总结(一):基本用法与类型擦除 J ...

  7. Java泛型学习资料小汇

    <Effective Java>的第二版,第5章 泛型 ★★★★★ 2008年5月出的<Effective Java>的第二版涵盖了Java SE 5和Java SE 6. 其 ...

  8. java 泛型应用,通用返回类,泛型方法,泛型静态方法

    java 泛型应用,通用返回类,泛型方法,泛型静态方法 泛型简介 应用示例 静态方法增加泛型参数 调用静态公有方法 对比调用非静态公有方法(成员方法) 泛型简介 这里不多说明,详见 java泛型入门 ...

  9. Java 泛型的本质——类型擦除

    文章目录 简介 Java泛型的类型擦除的证明例子 类型擦除到边界 擦除的代价与使命 使用泛型不是强制的 泛型代码边界的动作 非泛型类库和泛型类库:字节码一模一样 擦除的补偿 泛型与工厂模式 泛型数组 ...

最新文章

  1. Android 使用RxJava--基础篇
  2. 手机技巧:手机用一段时间就发烫,如何解决?
  3. 文件服务器共享文件夹访问权限,5对文件服务器中的共享文件夹进行访问权限控制...
  4. [转载] JAVA 构造函数及其重载
  5. 去掉网页上链接或按钮的虚线框
  6. C/C++线程与多线程工作笔记0007---单线程实现文件查找系统
  7. MyBatis-Plus 代码生成器报错
  8. 二进制文件转成文本保存,并可以读回
  9. Java基础:红黑树概念
  10. cannot spawn XXX\bin\TortoiseGitPlink.exe: No such file or directory
  11. android电子时钟码源,android自定义时钟APP源码
  12. python游戏设计毕业论文_游戏毕业设计论文
  13. php五行万年历,PHP制作万年历
  14. 外贸常用术语_2017常用外贸术语大全
  15. 2016到2019,百度大脑持续刷新
  16. Ectiture impossible,doublon dans une cle de la错误
  17. 教室录播系统方案_精品课程录播教室建设方案
  18. PANGU 生态乐园 NFT 系列上线 The Sandbox 市场平台
  19. C++switch语句 | 判断某年某月有几天
  20. stagefright框架(三)-選擇Video Decoder

热门文章

  1. 对属性可以赋值的位置
  2. React中构造函数、reader和函数的调用次数和时机测试
  3. 2018蓝桥杯省赛---java---A--2-(星期一)
  4. centos7离线安装oracle11g,CentOS 7.5离线安装Oracle 11gR2
  5. 什么叫做在oracle目录下,ORACLE directory 目录
  6. python调用webservice接口实例_python调用webservice接口的实现
  7. ibatis(2)ibatis是什么
  8. 纯注解开发配置spring
  9. junit junit_JSON的JUnit Hamcrest Matcher
  10. java更好的语言_五个使Java变得更好的功能