2020年对Java有何期待?

2020年已经如火如荼,让我们讨论一下今年Java世界中正在发生的变化。本文将列出Java和JDK的主要趋势。我将对读者在评论中的补充内容感到满意。

立即保留该文章更具有事实调查性的保留。可以在相应项目的网站或开源出版物中找到有关所讨论的每个主题的详细信息。

图片

所以,让我们开始吧。不幸的是,您将立即使那些不太遵循Java发布周期但正在等待长期支持程序(LTS)的人感到失望。今年,我们正在等待只有较短的支持生命周期(STS)的版本。
首先,我们将考虑即将于3月中旬发布的JDK 14版本。在此发布周期中,多达16个JEP被声明。以下是完整列表:
305:instanceof的模式匹配(预览)
343:包装工具(培养箱)
345:G1的NUMA感知内存分配
349:JFR事件流
352:非易失性映射字节缓冲区
358:有用的NullPointerExceptions
359:记录(预览)
361:开关表达式(标准)
362:弃用Solaris和SPARC端口
363:删除并发标记扫描(CMS)垃圾收集器
364:Mac OS上的ZGC
365:Windows上的ZGC
366:弃用ParallelScavenge + SerialOld GC组合
367:删除Pack200工具和API
368:文本块(第二预览)
370:外部存储器访问API(孵化器)

这份清单中的许多JEP在2019年Joker大会上得到了广泛报道,我将重点介绍对我而言最有趣的那些。

instanceof的模式匹配(预览)


漫长的JEP终于在Preview中问世。我认为,如果您是一位从事Java代码多年的实践程序员,那么您将不只一次遇到这种痛苦:

if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.toUpperCase());
}

如果您也使用Kotlin编写或正在编写代码,那么看到Java代码的痛苦会更糟。琥珀项目的参与者将向我们展示他们对Java模式匹配的看法,这将减轻这种痛苦。随着Java 14的问世,我们可以将示例重写如下:

if (obj instanceof String s) {
   System.out.println(s.toUpperCase());
}

似乎该插件没有那么有价值-我们节省了一行代码。但是假设我们要执行以下操作:

if (obj instanceof String) {
    String s = (String) obj;
    if (s.contains(“prefix_”)) {
       return s.toUpperCase();
    }
}
return null;

它看起来笨重,不是吗?让我们尝试相同的事情,但是要使用模式匹配。

return (obj instanceof String s) && s.contains(“prefix_”) ? s.toUpperCase() : null;

所以显然会更好。但是请记住,此功能的状态为“预览”。让我们看看随着时间的变化。对我来说,这绝对会让我的生活更美好。

有用的NullPointerExceptions


2020年就在院子里,您仍然在写,以便NullPointerExceptions为您而飞?别担心,您可能不是唯一的一个。Goetz Lindenmaier和Ralf Schmelter并未提出摆脱NullPointerExceptions的新方法(可选仍然是我们),但他们建议改进应用程序的调试过程以准确了解null的位置。因此,让我们想象一下,我们当然是在晚上五点钟编写代码的。我们编写了以下函数:

public String getStreetFromRequest(Request request) {
   return request.getAddress().getStreet().toUpperCase();
}

不错,但是他们完全忘记了放置@Nullable和@Nonnull批注,并检查传输字段中的地址。得到了NullPointerException。异常告诉我们什么?

Exception in thread "main" java.lang.NullPointerException
	at Program.getStreetFromRequest(Program.java:10)
	at Program.main(Program.java:6)

las,我们只能看到一行,一个类和一个堆栈。空值到底返回哪里?也许这是一个请求?也许getAddress()返回null?还是getStreet()?好吧,呼叫链有时会受伤。

JEP的作者提出了一个解决方案:抛出异常时,应该绕过堆栈来确定返回的确切位置为null,然后显示变量/方法的名称。让我们尝试使用-XX:+ ShowCodeDetailsInExceptionMessages选项的Java 14。我们开始时会有所不同:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because the return value of "Address.getStreet()" is null
	at Program.getStreetFromRequest(Program.java:10)
	at Program.main(Program.java:6)

现在我们知道夜间编程不会带来好处(但有时会导致按时完成任务),并且在我们的程序中我们忘记了地址不是必填字段。

记录(预览)


还在产生带有想法的getters / setters / equals / hashCode吗?然后这个JEP来找你!
数据类距离应用程序软件开发人员生命中的最后位置很远。每次我们必须使用我们最喜欢的IDE生成Data类的方法时,或者使用各种编译时插件来生成必要的方法,例如lombok。

结果,我们有很多与此类似的代码:

public class Request {
    private Address address;

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
       this.address = address;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Request request = (Request) o;
        return Objects.equals(address, request.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(address);
    }

    @Override
    public String toString() {
        return "Request{" +
                "address=" + address +
                '}';
    }
}

或诸如此类:
@Data
@AllArgsConstructor
public class Request {
    private Address address;
}

在Java 14中,Amber项目的成员提出了一种用于创建数据类的新语法。为此,请使用新的关键字记录。Record的语法与类描述或枚举稍有不同,并且与Kotlin略有相似。上面的代码可以重写如下:

public record Request(Address address) {
}

默认情况下,所有记录字段都具有私有修饰符和最终修饰符。记录本身是最终类,不能从另一个类继承,但是它可以实现接口。在框中的记录类中,我们得到:getters方法,一个公共构造函数,其参数均为描述顺序中的所有记录字段,等于/ hashCode和toString。令人不愉快的是:除了在类名之后指定的字段外,我们无法将字段添加到记录中。例如,此代码将导致错误:

public record Request(Address address) {
   private final String anotherParameter; // compilation error
}

文本块(第二预览)


文本块在Java 13中作为预览发布。我们用户扭曲,旋转并提供了反馈(我真诚地相信您已经在使用Java 13和预览功能)。结果,Java创建者对文本块进行了较小的更改。首先,我们现在可以通过在行中添加转义序列\来明确指示行的结尾。这是一个例子:

public static void main(String[] args) {
        final var test = """
                This is the long text block with escape string \s
                that is really well done            \s
                """;
        System.out.println(test);
}

现在,我们将所有空格显式设置为\字符,所有空格字符将保存为\ s字符。

其次,现在我们可以包装文本块的长行,而在最后一行不接收\ n个字符。为此,我们只需要在换行符处添加\。它是什么样子的:

public static void main(String[] args) {
        final var test = """
                This is the long text block with escape string \
                that is really well-done functional            
                """;
System.out.println(test);

执行后,我们得到以下行:“这是带有转义字符串的长文本块,功能确实很好。”

在我看来,这是一个很好的补充。我非常期待将此功能转换为标准功能。
我们审查的所有功能都可能在即将召开的会议上进行广泛讨论。其中一些已经在Joker 2019上进行了讨论。请务必查看Cay Horstmann在Joker 2019上发表的关于“ Java 13及更高版本中的功能进化”的演讲。

还有一些更有趣的事情。


孵化器中JEP列表上有两个有趣的项目。首先,我们将拥有一个通用工具,该工具将为不同的操作系统创建安装程序(最后,我想告诉那些在Windows上安装程序的人)。 Jpacker将能够为Windows创建maci / exe安装程序,为Linux创建macOS软件包和rpm / deb。让我们看看结果如何,但是在极少数情况下,当我为台式机做某事时,我个人遭受了这样一个事实,即我没有用于安装安装程序的常规工具。

更有前景的是用于访问“外部内存”的新API,即 任何类型的本机或持久性内存。例如,此API主要对Java数据库创建者或Netty等框架创建者有用。他们使用Unsafe和ByteBuffer来尽可能利用堆外资源优化内存访问。

下一个版本。喜悦与沮丧


9月,我们正在等待第15号的另一个短期支持版本。最终版本中将包括的JEP列表仍处于打开状态。到目前为止,您可以在标准库和虚拟机中看到语言本身的许多不同更改。
这是候选人列表(可以快速更改,请看这里:bugs.openjdk.java.net/secure/Dashboard.jspa?selectPageId=19114):
111:Additional Unicode Constructs for Regular Expressions
116:Extended Validation SSL Certificates
134:Intuitive Semantics for Nested Reference Objects
152:Crypto Operations with Network HSMs
198:Light-Weight JSON API
218:Generics over Primitive Types
300:Augment Use-Site Variance with Declaration-Site Defaults
301:Enhanced Enums
302:Lambda Leftovers
303:Intrinsics for the LDC and INVOKEDYNAMIC Instructions
306:Restore Always-Strict Floating-Point Semantics
338:Vector API (Incubator)
339:Compiler Intrinsics for Java SE APIs
348:Compiler Intrinsics for Java SE APIs
356:Enhanced Pseudo-Random Number Generators
360:Sealed Types (Preview)
371:Hidden Classes

如您所见,该列表仍然没有太多期望的东西。首先,对我来说是Project Loom。近年来,结构并行的想法非常流行。协程可以大大简化竞争性并行计算和任务的异步执行的任务。可以在例如Kotlin(协程)和Go(goroutines)语言中看到实现此想法的绝佳示例。 Java也在探索结构并行性的思想,并且已经有了初步结果。目前,您只能通过从项目存储库编译JDK来查看它们。

一个非常有前途的项目Valhalla还没有通过预览使我们满意。关于这个项目的有趣报告在Joker 2019上发表了(Sergey Kuksenko的“ Java是否需要内联“类型?Valhalla项目的性能工程师的狭窄视角”)。

列表中显示了什么?

引起您注意的第一件事是JSON API。问题立即出现-为什么?显然没有明确的答案。关于动机的JEP部分说,JSON已经成为Web服务的标准,现在是时候使Java SE与JSON交互了(即使现在有很多库可以解析JSON)。最可能的解释是软件开发人员能够使用小型核心API来减少捆绑包的大小,而不必将沉重的Jackson拖到自己身上。我看不到任何其他解释,因为它甚至没有数据绑定。

我们还看到了与加密API相关的许多改进。首先,JDK开发人员希望通过添加对EVSSL证书的支持来扩展验证SSL证书的过程。使用Java中的该API,您可以确定EV证书(扩展验证)是否信任SSL连接。将完全支持符合指南的EVSSL证书。还将添加新的EdDSA加密算法,并将改进HSM加密的验证。

从语言的角度来看,我将选择泛型在基元上的实现。每个曾经用C#编程并切换到Java的人都可能会问一个问题,为什么不可能对基元执行泛型类型。答案很简单-泛型仅适用于对象,而基元不是对象,尤其是不继承Object类。这不是在这一问题上展开战争的第一年,而布莱恩·格茨(Brian Goetz)又回到了这一问题。到目前为止,没有什么特别的描述。任务很明确-支持诸如List之类的构造。但是即使在目前,在实现此功能之前,也需要解决13个未解决的问题。老实说,我想知道这个系列会如何结束。

我最后要谈的是密封类型。这是进行模式匹配的下一步。密封类型是实现密封(修饰符)并允许类或接口使用关键字的语言扩展。

使用密封类,我们将后代的数量限制为仅在许可(显式限制)或同一编译单元(文件)中指定的那些类。密封类的示例描述:

// 
public abstract sealed class Base {
   public static class ChildA extends Base{}
   public static class ChildB extends Base{}
}

// 
public sealed interface BaseInterface permits ChildC, ChildD{
}

//  
public class ChildC implements BaseInterface {
}
//  
public class ChildD implements BaseInterface {
}

密封修饰符可确保只有有限的后代集合才能扩展基类或实现接口。处理这些类的对象时可以使用此属性。而且,当然,这是在模式匹配的switch语句中使用的最佳选择。

结论


我们研究了今年的各种JDK创新。其中有些会射击,有些不会。但是,在所有新JDK中,最重要的是,我希望有新的小型(或并非如此)的优化能够使我们的程序在每个版本免费的情况下都更快。如果您在2019年最后一个Joker上访问了Tagir Valeev Java 9-14:小型优化的报告,那么像我一样,您很可能会对贡献者优化JDK所做的工作印象深刻。乍看之下看不到它的结果,也没有一个以上的JEP反映出来,但是我们每天都在使用它们。

好的Java发行给了我们所有人。探索新的平台功能,参加会议并跟踪趋势。

All Articles