java 泛型嵌套泛型

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

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

如果有人错误地通过以下方式创建了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 泛型嵌套泛型

java 泛型嵌套泛型_Java泛型简介–第6部分相关推荐

  1. java 接口的泛型方法_Java泛型/泛型方法/通配符/泛型接口/泛型泛型擦出

    从JDK1.5以后引入了三大常用新特性:泛型.枚举(enum).注解(Annotation).其中JDK1.5中泛型是一件非常重要的实现技术,它可以帮助我们解决程序的参数转换问题.本文为大家详细介绍一 ...

  2. java 泛型集合应用_Java泛型集合的应用和方法

    展开全部 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以62616964757a686964616fe78988e69d8331333 ...

  3. java 怎么为泛型参数赋值_Java泛型讲解

    1. 概述在引入泛型之前,Java类型分为原始类型.复杂类型,其中复杂类型分为数组和类.引入范型后,一个复杂类型 就可以在细分成更多的类型. 例如原先的类型List,现在在细分成List, List等 ...

  4. java泛型常用特点_Java泛型详解

    对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下. 本文参考java 泛型详解.Java中的泛型方法. java泛型详解 1. 概述 泛型在 ...

  5. java泛型与接口_Java泛型用于方法,类和接口

    什么是泛型? 型就是数据类型参数化.就是说原本定死的数据类型像方法中的形参一样,数据是不确定的,使用的时候由人传进去,泛型就是这样,数据类型不确定了.使用的时候再确定就可以了. 泛型的特点 是JDK1 ...

  6. java参数传入泛型类型_Java 泛型(参数化类型)

    Java 泛型 Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所 ...

  7. java 泛型集合示例_Java 泛型(示例代码)

    1.泛型的由来 我们先看下面这段代码: 报错信息如下: 也就是 集合中第二个数据是 Integer,但是我们取出来的时候将其转换为 String 了,所以报错. 那么这个如何解决呢? ①.我们在遍历的 ...

  8. java泛型详解_Java泛型详解(透彻)

    定义 Java中的泛型在JavaSE5中引入. 所谓泛型,即参数化类型.就是说,类型是以参数的方式传入泛型类. 例如: ArrayList aaryList = new ArrayList(); 那么 ...

  9. 描述java泛型引入原则_Java/泛型的类型擦除/README.md · oslo/LearningNotes - Gitee.com

    前言 Java 泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,但有一点需要注意:Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类型在编译后都会被清除掉,看下 ...

  10. java super extend 区别_Java泛型中extends和super的区别

    extends T>和 super T>是Java泛型中的"通配符(Wildcards)"和"边界(Bounds)"的概念. extends T&g ...

最新文章

  1. leetcode算法题--矩阵区域和
  2. node exprass安装运行实例
  3. CF1096E The Top Scorer
  4. gogs可以自动化部署吗_Git-WebHook 自动化部署工具 - 支持Github / GitLab / Gogs / GitOsc...
  5. java 中map_Java中Map集合
  6. linux 判断网卡是否异常_如何判断linux网卡故障?
  7. Python获取命令行参数
  8. 【ElasticSearch】Es 源码之 Discovery DiscoveryModule ZenDiscovery 源码解读
  9. MYSQL索引和权限管理
  10. Eclipse安装WindowsBuilder失败解决方法
  11. Part.0模糊集表示方法
  12. 智能会议系统集成解决方案
  13. 吉客云与金蝶云星空集成方案(吉客云主管库存)
  14. html中图片旋转木马,教你怎么用CSS3做一个图片的旋转木马效果
  15. ARP报文的存入条件和回复条件
  16. Android Studio MAC M1 安装模拟器
  17. XZ压缩最新压缩率之王
  18. MATLAB上的GPU加速计算
  19. 江湖中常说的“网格交易法”到底是什么?
  20. 美团/叮咚/盒马/淘鲜达/百联到家autojs脚本学习案例

热门文章

  1. jzoj1265-Round Numbers【数位统计】
  2. codeforces773 D. Perishable Roads(思维+最短路)
  3. E - Escape from the Island(最短路+dp)
  4. 【记忆化搜索】【线性化DP】滑雪 (ssl 1202/luogu 1434/pku 1088)
  5. Wannafly挑战赛24
  6. Scala与Java差异(三)之函数
  7. 深入源码分析Java线程池的实现原理
  8. Java中的6颗语法糖
  9. 花了100多去KTV不是唱歌,竟然是……
  10. idea tomcat部署web项目_项目开发之部署帆软到Tomcat服务一