Java 5已将通用多态性引入Java生态系统。 即使我们都知道由于泛型类型擦除及其后果而引起的无数警告,这对Java语言还是一个很大的补充。 通用多态性(也称为参数多态性 )通常与可能预先存在的亚型多态性正交。 一个简单的例子是collections API

List<? extends Number> c = new ArrayList<Integer>();

在上面的示例中,子类型ArrayList被分配给超类型List的变量。 同时,使用Integer类型对ArrayList进行参数化,可以将其分配给兼容的参数超类型? extends Number ? extends Number 。 在泛型多态性的上下文中这种子类型多态性的用法也称为协方差 ,尽管当然也可以在非泛型上下文中实现协方差。

具有通用多态性的协方差

协方差对于泛型很重要。 它允许创建复杂的类型系统。 简单的示例涉及将协方差与通用方法结合使用:

<E extends Serializable> void serialize(Collection<E> collection) {}

上面的示例接受任何Collection类型,可以在调用站点使用诸如ListArrayListSet等类型将其子类型化。 同时,仅要求调用站点上的泛型类型参数是Serializable的子类型。 即它可以是List<Integer>ArrayList<String>等。

将亚型多态性与通用多态性相关联

于是人们常常被引诱去关联两种正交类型的多态性。 此类关联的一个简单示例是将IntegerListStringSet专门IntegerList

class IntegerList extends ArrayList<Integer> {}
class StringSet extends HashSet<String> {}

这是很容易看到明确的类型的数量会爆炸,如果你开始跨越亚型和泛型类型层次的笛卡尔积,希望通过创建之类的东西更精确地专门IntegerArrayListIntegerAbstractListIntegerLinkedList等。

使相关性通用

从上面可以看出,这种关联通常会从类型层次结构中删除通用性,尽管并不需要这样做。 在以下更一般的示例中可以看出:

// AnyContainer can contain AnyObject
class AnyContainer<E extends AnyObject> {}
class AnyObject {}// PhysicalContainer contains only PhysicalObjects
class PhysicalContainer<E extends PhysicalObject>extends AnyContainer<E> {}
class PhysicalObject extends AnyObject {}// FruitContainer contains only Fruit,
// which in turn are PhysicalObjects
class FruitContainer<E extends Fruit>extends PhysicalContainer<E> {}
class Fruit extends PhysicalObject {}

上面的示例是一个典型的示例,其中诱使API设计人员将子类型多态性( Fruit extends PhysicalObject extends AnyObject )与通用多态性( <E> )相关联,同时保持通用性,从而允许在FruitContainer添加其他子类型。 AnyObjectAnyObject应该知道其自己的子类型时,这会变得更加有趣。 这可以通过递归泛型参数来实现。 让我们修复前面的示例

// AnyContainer can contain AnyObject
class AnyContainer<E extends AnyObject<E>> {}
class AnyObject<O extends AnyObject<O>> {}// PhysicalContainer contains only PhysicalObjects
class PhysicalContainer<E extends PhysicalObject<E>>extends AnyContainer<E> {}
class PhysicalObject<O extends PhysicalObject<O>>extends AnyObject<O> {}// FruitContainer contains only Fruit,
// which in turn are PhysicalObjects
class FruitContainer<E extends Fruit<E>>extends PhysicalContainer<E> {}
class Fruit<O extends Fruit<O>>extends PhysicalObject<O> {}

这里有趣的部分不再是容器,而是AnyObject类型层次结构,该结构将子类型多态与自己类型上的通用多态相关联! 这也可以通过java.lang.Enum完成:

public class Enum<E extends Enum<E>>
implements Comparable<E> {public final int compareTo(E other) { ... }public final Class<E> getDeclaringClass() { ... }
}enum MyEnum {}// Which is syntactic sugar for:
final class MyEnum extends Enum<MyEnum> {}

危险所在?

枚举和我们的自定义之间的细微差别AnyObject层次是事实MyEnum通过被终止的两个正交分型技术递归自相关final ! 另一方面,除非将AnyObject子类型也AnyObject为最终类型,否则不应将其删除。 一个例子:

// "Dangerous"
class Apple extends Fruit<Apple> {}// "Safe"
final class Apple extends Fruit<Apple> {}

为什么final如此重要,或者换句话说,为什么在终止递归自相关(例如Apple之前)时必须小心AnyObject子类型? 这很简单。 让我们假设以下添加:

class AnyObject<O extends AnyObject<O>>implements Comparable<O> {@Overridepublic int compareTo(O other) { ... }public AnyContainer<O> container() { ... }
}

上述关于AnyObject.compareTo()约定意味着, AnyObject任何子类型只能与同一子类型进行比较。 以下是不可能的:

Fruit<?> fruit = // ...
Vegetable<?> vegetable = // ...// Compilation error!
fruit.compareTo(vegetable);

层次结构中当前唯一可比较的类型是Apple:

Apple a1 = new Apple();
Apple a2 = new Apple();a1.compareTo(a2);

但是,如果我们想添加GoldenDeliciousGala苹果怎么办?

class GoldenDelicious extends Apple {}
class Gala extends Apple {}

我们现在可以比较它们!

GoldenDelicious g1 = new GoldenDelicious();
Gala g2 = new Gala();g1.compareTo(g2);

这不是AnyObject作者的AnyObject

这同样适用于container()方法。 允许子类型协变地专门化AnyContainer类型,这很好:

class Fruit<O extends Fruit<O>>extends PhysicalObject<O> {@Overridepublic FruitContainer<O> container() { ... }
}

但是, GoldenDeliciousGalacontainer()方法会GoldenDelicious

GoldenDelicious g = new GoldenDelicious();
FruitContainer<Apple> c = g.container();

是的,它将返回一个Apple容器,而不是AnyObject设计人员想要的GoldenDelicious容器。

子类型多态和泛型多态跨越正交类型轴。 使它们相互关联可能是类型系统中的设计气味。 使它们在同一类型上关联是危险的,因为很难正确处理。 用户将尝试终止基本类型的子类型上的递归泛型类型定义。 终止的原因是具有递归自绑定的基本类型很难使用的事实。 但是终止通常会出错,因为它只能在final类上执行,而不能在常规类或接口上执行。

换句话说,如果您认为需要基于通用基类型的递归泛型类型定义,请仔细考虑,如果确实需要,并且您的类型用户可以在final类中正确终止递归泛型类型定义。

参考:来自JAVA,SQL和JOOQ博客的JCG合作伙伴 Lukas Eder提出的将子类型多态与通用多态相关联的危险 。

翻译自: https://www.javacodegeeks.com/2013/07/the-dangers-of-correlating-subtype-polymorphism-with-generic-polymorphism.html

将亚型多态性与通用多态性相关联的危险相关推荐

  1. tcga癌症亚型获取_将亚型多态性与通用多态性相关联的危险

    tcga癌症亚型获取 Java 5已将通用多态性引入Java生态系统. 即使我们都知道由于泛型类型擦除及其后果而引起的无数警告,这还是对Java语言的重要补充. 通用多态性(也称为参数多态性 )通常与 ...

  2. Week 4.2 | Lecture 10 | 多态性设计通用方法、比较器 | CS61B-Spring-2018

    一.多态性 多态性:多态性的核心是"多种形式".在Java中,多态性是指对象如何具有多种形式或类型.在面向对象的编程中,多态与如何将一个对象视为其自己的类的实例,其超类的实例,其超 ...

  3. 软件工程习题,耦合性的概念如何与软件可移植性相关联?举例支持你的论述,应用逐步求精法为下列程序开发三种不同级别的过程抽象

    软件工程习题 11.9.耦合性的概念如何与软件可移植性相关联?举例支持你的论述 11.10.应用逐步求精法为下列程序开发三种不同级别的过程抽象,开发一个支票打印程序,给出输出金额,并按支票常规要求给出 ...

  4. tcga癌症亚型获取_亚型多态性应用于元组的危险

    tcga癌症亚型获取 Java 8具有lambda和stream,但是没有元组,这真是令人遗憾 . 这就是为什么我们在jOOλ中实现了元组-Java 8的缺失部分 . 元组确实是无聊的值类型容器. 本 ...

  5. 设计代码说明什么是多态性?如何实现多态?(代码中要写注释解释)_狗屎一样的代码!快,重构我...

    关注后你就是我的人了 重构不止是代码整理,它提供了一种高效且受控的代码整理技术. (一)重构原则 1.何谓重构 对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修 ...

  6. 解耦 多态性 java_Java的多态浅谈,Java多态浅谈网站安全分享!

    Java的多态浅谈概述Java的四大基本特性:抽象,封装,继承和多态.其中,抽象,封装,继承可以说多态的基础,而多态是封装,继承的具体表现.如果非要用专业术语来描述什么是多态的话 多态是指程序中定义的 ...

  7. 亚型多态性应用于元组的危险

    Java 8具有lambda和stream,但是没有元组,这真是令人遗憾 . 这就是为什么我们在jOOλ中实现了元组-Java 8缺少的部分 . 元组确实是无聊的值类型容器. 本质上,它们只是这些类型 ...

  8. MyBatis-Plus通用枚举自动关联注入

    一.通用枚举 解决了繁琐的配置,让 mybatis 优雅的使用枚举属性! 一般搜索用户信息列表,列如用户有禁用和启用两个状态 @Data public class User implements Se ...

  9. 设计代码说明什么是多态性?如何实现多态?(代码中要写注释解释)_狗屎一样的代码!快,重构我!...

    狗屎一样的代码如何重构? 重构不止是代码整理,它提供了一种高效且受控的代码整理技术. (一)重构原则 1.何谓重构 对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低 ...

最新文章

  1. vb 变量赋值为当前选定单元格_VBA变量5年踩坑吐血精华总结
  2. Dockerfile命令详解
  3. android 抛出异常,android - android Build.GetSerial()抛出异常 - 堆栈内存溢出
  4. 数据库中Schema(模式)概念的理解
  5. iOS modal view的关闭和显示问题
  6. 通过锂电池原理来设计BMS系统
  7. 关于安装软件时x86 ,x64,x86_64,ARM 64, ARM 32 的选择
  8. java 区分中英文_Java 区分文本中的中英文字符函数
  9. 嵌入式单片机基础篇(二十三)之串口通信
  10. MacBook下使用VirtualBox虚拟Win7时设置分辨率为2560*1440
  11. dbo.GetJsonValue函数
  12. 【粉丝福利、免费分享】7900本开源电子书、400份优质PPT模板、100份优质简历模板、100张思维导图 免费分享!
  13. Oracle 表空间(tablespace)
  14. ubuntu火狐浏览器安装flash_player插件
  15. 嵌入式工程师应该有的工作态度
  16. 微信互动大屏 霸屏 抢红包 源码及所需技术
  17. android stk流程,Android STK 实现
  18. 全网最优质的H3C设备命令大全
  19. 闲聊飞思卡尔ADC模块
  20. 为什么要考Martin Fowler的年龄-《软件方法》自测题解析014

热门文章

  1. android 枚举类型比较大小写,Spring 3.0 MVC绑定枚举区分大小写
  2. 为什么光标停在表格中间_word里面为什么打出来的数字中间为啥差一个光标的距离 - 卡饭网...
  3. Spring boot(六):如何优雅的使用mybatis
  4. jstat分析_jstat –分析
  5. java监控数据库性能_Java:GraalVM数据库流性能
  6. spring mvc教程_Spring MVC教程
  7. java 方法 示例_Java 9示例–收集的工厂方法–创建不可修改的列表,集合和映射...
  8. 性能测试中脚本怎么写_脚本在流程中的性能影响
  9. apache lucene_Apache Lucene的结构
  10. java数字格式化_Java数字格式