博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
新JEP将简化Java类型变异
阅读量:7168 次
发布时间:2019-06-29

本文共 3362 字,大约阅读时间需要 11 分钟。

旨在简化处理Java中复杂的类型变异的概念。这个新的JEP Candidate可能会在Java 10中推出,提供了在定义的泛型类型中指定目标对象默认变异的方法,而不是在泛型类型实例化时通过通配符指定。这个新方案并不会代替通配符,而是减少对通配符的需求。

\\

类型变异这个概念对于很多开发人员来说仍然比较模糊,在Java中通过不太普及的通配符来解决这个问题并没有很大帮助。因此,为了帮助我们的读者能够理解这款JEP的潜在影响力,在本文中我们将首先解释什么是类型变异,目前Java中是怎么解决它的,之后将介绍这个新方案能实现什么。

\\

变异、协变和逆变

\\

以下的代码属于传统的在线购物应用程序:

\\
public class Product {\/* ... */\}\\public class FrozenProduct extends Product {\    /* ... */\}\
\\

如果有个方法scan(Product product),我们调用它传递FrozenProduct对象,调用工作没有问题,这是众所周知的多态性的一般规则。但是当参数中包含泛型时,就不能使用相同的逻辑,以下的代码将无法编译:

\\
private void addAllProducts(List\u0026lt;Product\u0026gt; products) {\/* Check product stock */\shoppingCart.addAll(products);\}\\private void freezerSection() {\    List\u0026lt;FrozenProduct\u0026gt; frozenProducts = /* select frozen products */;\    addAllProducts(frozenProducts); // ERROR: List\u0026lt;Product\u0026gt; expected\                                    //        List\u0026lt;FrozenProduct\u0026gt; found\}\
\\

当在Java中使用泛型时,并没有关于目标类型和其子类型或超类型之间兼容性的假设。换句话说,当使用泛型时,我们默认目标类型是不变的,它只接受明确的类型。

\\

然而,在上面的例子中,我们可以看到addAllProducts方法可以处理List of Product或是其子类型。当泛型参数可以接受其目标类型或是它的任何子类型,我们就说这个类型是协变的,在Java中可以用extends表示:

\\
private void addAllProducts(List\u0026lt;? extends Product\u0026gt; products) {\/* Check product stock */\shoppingCart.addAll(products);\}\\private void freezerSection() {\    List\u0026lt;FrozenProduct\u0026gt; frozenProducts =  /* select frozen products */;\    addAllProducts(frozenProducts); // works with no problem\}\
\\

在这些例子中,接受的目标类型的变异是子类型。在一些其他例子中,目标类型的变异不是子类型,而是超类型。考虑以下的情况:

\\
private boolean askQuestion(Predicate\u0026lt;String\u0026gt; p) {\return p.test(\"hello\");\}\\private void applyPredicate() {\    Predicate\u0026lt;Object\u0026gt; evenLength = o -\u0026gt; o.toString().length() % 2 == 0;\    askQuestion(evenLength); // ERROR: Predicate\u0026lt;String\u0026gt; expected\                             //        Predicate\u0026lt;Object\u0026gt; found\}\
\\

在这种情况下,我们可以看到使用string “hello”到lambda o -\u0026gt; o.toString().length() % 2 == 0中不会发生问题,然而,编译器不允许我们这么做。askQuestion可以处理Predicate of String或其任意超类型:我们就说这种情况下的目标类型是逆变的,在Java中可以用super表示:

\\
private boolean askQuestion(Predicate\u0026lt;? super String\u0026gt; p) {\return p.test(\"hello\");\}\\private void applyPredicate() {\    Predicate\u0026lt;Object\u0026gt; evenLength = o -\u0026gt; o.toString().length() % 2 == 0;\    askQuestion(evenLength); // works with no problem\}\
\\

通配符是创建类型变异的一种非常灵活的方法,因为它允许你在不同地方对同种类型定义不同的变异。比如说,在上面的例子里我们定义addAllProducts是协变的参数,但在其他地方根据我们的需求,可以定义它为逆变或是不变的。然而缺点是必须在每个地方根据需要明确指定变异,这样会造成很多的冗余和混乱。所以新的方案应运而生。

\\

在声明时指定默认变异

\\

通配符的最主要的问题是它们比开发人员通常需要的还要灵活。在Predicate\u0026lt;String\u0026gt;的例子中,我们理论上可以创建一个方法Predicate\u0026lt;? extends String\u0026gt;,然而,可以用到的用例有限(可能根本没有)。在大量情况下,只有一个类型变异有意义,为了反映出这一点,提供了在声明泛型类型时指定默认变异的方法,而不是在实例化时指定默认变异。比如说,用了这种方案,可以使用逆变的关键字Predicate\u0026lt;contravariant T\u0026gt;来重写接口Predicate\u0026lt;T\u0026gt;,这就代表着任何时候开发人员写Predicate\u0026lt;String\u0026gt;都会被隐含地理解为Predicate\u0026lt;? super String\u0026gt;

\\

这个新功能的语法尚未决定,但是已经有了一些备选项:使用新的显式关键字,如Function\u0026lt;contravariant T, covariant R\u0026gt;,或遵循其他语言的先例,如Scala中的符号(Function\u0026lt;-T, +R\u0026gt;),或是C#中的较短关键字(Function\u0026lt;in T, out R\u0026gt;)。在解决语法问题之前,还需要解决一些重要的技术问题,即默认变异和通配符之间的交互,默认变异对现有代码产生的影响,以及变异类型兼容性检查的实际机制。

\\

最后值得提出的一点是,JEP 300仅会处理新的默认变异,但不会修改Java库中可用的任何类和接口。如果之后JEP 300再发展可能会考虑处理这种情况,但也只是在其他版本的JEP中执行。

\\

查看英文原文

转载地址:http://gtqwm.baihongyu.com/

你可能感兴趣的文章
我的友情链接
查看>>
Truncate/Delete/Drop table的特点和区别
查看>>
我的友情链接
查看>>
nginx http core模块学习
查看>>
逢二进一 、逢八进一、逢十六进一
查看>>
搞懂 JAVA 内部类
查看>>
Android中创建与几种解析xml的方法!
查看>>
程序员有趣的十八个事实
查看>>
数据库导出数据字典
查看>>
scala中的option[T]、Any、Nothing、Null和Nil
查看>>
面试算法
查看>>
activemq cluster安装
查看>>
zabbix snmp 常见OID
查看>>
spring cloud 与 docker-compose构建微服务
查看>>
Ext4 Disk Layout
查看>>
rrdtool 详解
查看>>
firefox NS_ERROR_DOM_BAD_URI: Access to restricted URI denied
查看>>
Git常见相关知识与命令
查看>>
lvs(Linux Virtual Server)浅析----------------lvs-nat和lvs-dr实现
查看>>
PostgreSQL pg_rewind report error退出分析
查看>>