JavaRush /Java 博客 /Random-ZH /喝咖啡休息#37。Java 的新未来。2020 年后 JVM、Kotlin 和 Java 前景

喝咖啡休息#37。Java 的新未来。2020 年后 JVM、Kotlin 和 Java 前景

已在 Random-ZH 群组中发布
来源:Medium Java 主要因两件事而受到批评:冗长以及在许多情况下生成的大量样板代码,但没有明显的需要。虽然我一直很喜欢Java,但我不能说这些说法是错误的。确实如此:Java 中过多的细节有时会非常烦人。然而,我们必须承认,我们并不生活在一个理想的世界,大多数情况下我们必须两害相权取其轻。喝咖啡休息#37。 Java 的新未来。 2020 年后 JVM、Kotlin 和 Java 前景 - 1自诞生以来,Java 并不完美:我们都知道这一点,但真正的问题是为什么之前没有采取任何措施来解决这些问题。我认为改变花了这么长时间的唯一原因是 Java 语言缺乏竞争,事情就这样了。Java 占据了市场主导地位,这可能是由于缺乏强有力的竞争对手以及 Sun 和 Oracle 的巨大努力。Java 提供的高水平类型安全性和良好的语言结构使其在大型项目中非常受欢迎。此外,它是一种在自己的虚拟机上运行的多平台语言。与通过 JIT 编译器进行的自动性能优化相结合,所有这些都可以最大限度地减少编写不良代码的影响。总而言之,这是使用 Java 的一系列非常令人信服的理由。但接下来发生了什么?所发生的情况是,新的语言已经进入市场,可以在与 Java 相同的 JVM 中运行。这些语言消除了 Java 中一些最大的不便,并且在某些情况下为开发人员提供了一个更愉快的环境,并且进入门槛更低。在继续之前,我们先回顾一下并简要回顾一下 JVM 语言的历史。

JVM 语言的历史

首先,我想澄清一件事:我没有提及一些现有的 JVM 语言,因为它们从未获得足够的支持,无法被视为在我们行业中广泛使用的候选语言。现在让我们开始简要概述 JVM 语言的历史。首先我们要介绍的是 Java,它是 JVM 世界中最古老、最流行的语言。Java 于 1996 年 1 月正式发布,所以该语言已经存在了 24 年。不错吧?Java最初是一种纯粹的命令式语言,遵循面向对象的编程风格;它也是一种强类型语言。Java 的语法有点类似于 C++ 和 C 语言,但它被认为是改进版本,因为用 Java 编写代码比用 C 或 C++ 容易得多。另一方面,他的批评者最大的论点是冗长。第二种 JVM 语言是 Groovy。它自 2003 年就已存在,尽管其第一个标准化版本 1.0 直到 2007 年 1 月才出现。Groovy 的优点是它可以用作脚本语言。Groovy 是一种动态类型语言,因此类型检查是在运行时完成的。这就是一些开发人员不喜欢 Groovy 的原因之一。您用 Groovy 编写代码,在编译时看起来是正确的,但在运行时您意识到有些问题。然后另一种流行语言出现了:我们谈论Scala。它于 2004 年发布。他为 JVM 世界带来了一种新的工作模式:函数式编程和声明式方法。基本上,Scala 是第一个引入不变性概念的语言,然后用于增强 Java。另一方面,批评者不喜欢 Scala,因为它的语法复杂且可读性较低。JVM 世界中出现的下一种语言是 Clojure,一种纯函数式语言。尽管它早在 2007 年就出现了,但它最近变得相当流行。Clojure 是一种基于 LISP 的语言,其特点是简单且使用简单的函数。它的缺点之一是它是动态类型的(像 Groovy 一样)并且学习曲线要​​陡峭得多,因为它的语法与其他 JVM 语言完全不同。最后,我们有 Kotlin。Kotlin 于 2016 年 2 月首次出现,从那时起它的受欢迎程度就没有停止增长。它由 JetBrains 开发,主要目标是:解决最著名的 Java 问题。从设计上来说,Kotlin 保留了 Java 的所有优点,但同时解决了许多问题。这些是最重要的 JVM 语言。正如我所说,我们错过了一些其他不太流行的语言:Jython、JRuby、Ceylon、Fantom 等。如果您愿意,您可以找到现有 JVM 语言的完整列表在维基百科上。您可能已经意识到,Java 在其创建后的前八年或十年内并没有太多竞争,但从那以后情况发生了变化。那么,竞争是好事还是坏事呢?

增加竞争的好处

Java 在早期并没有发生太大变化。可能是因为没有必要。这种语言被广泛使用并且一直很受欢迎,尽管它远非完美。但随后竞争对手出现了,更现代的语言提供了新功能并解决了一些长期困扰 Java 开发人员的问题。例如,让我们看一下 Scala 语言。自 2009 年以来,Scala 的受欢迎程度一直在增长。开发人员欢迎这种新的功能风格,这给了他们更大的灵活性以及安全、轻松地编写并行代码的能力。甲骨文如何应对这一新趋势?2014 年,Java Lambda 和 Streams 出现。我想我们都同意,这是 Java 向击败 Scala 迈出的最大一步。现在任何程序员都知道 Scala 不再流行。喝咖啡休息#37。 Java 的新未来。 2020 年后 JVM、Kotlin 和 Java 前景 - 2在 JVM 领域拥有更多竞争对手的另一个好处是 JIT 编译器和 JVM 不断得到改进。现在越来越多的人对优化 JVM 和提高性能感兴趣。所以竞争对每个人都有好处!Java 的最新替代品是 Kotlin 语言。它的出现对于Java的发展非常重要,因为这种新语言在某种意义上为Oracle指明了前进的方向。Kotlin 示例表明,可以保留 Java 的优势,但可以创建一种更紧凑的语言,从而可以更快地编写代码。如果你查看 Google 趋势图,你可以看到从 2016 年到 2018 年,Kotlin 的受欢迎程度快速增长。但在过去的两年里,这种兴奋感有所下降。喝咖啡休息#37。 Java 的新未来。 2020 年之后的 JVM、Kotlin 和 Java 前景 - 3Oracle 仔细研究了业界对 Kotlin 的反应。如果您查看JDK 15 发行说明,您会发现一些新的 Java 功能是 Kotlin 中的功能的副本。这些是新的 Java 条目、新的文本块(带三引号的多行字符串)和新的运算符switch,它本质上是 Kotlin 中运算符的副本when。我们谈论的一切就是我所说的“Java 的 Kotlin 化”。通过成为更强大的竞争对手,Kotlin 向 Java 展示了应走的道路。

Java 的“Kotlin 化”

一些即将推出的 Java 功能将在可读性方面进行重大改进,并将解决 Java 语言的最大弱点之一 - 冗长。有人可能会说,许多已发布的 Java 功能与 Kotlin 的某些功能非常相似。但请注意,其中大部分是预发布版本。这意味着如果您安装 JDK 14 或 JDK 15(当其发布时),默认情况下您将无法使用它们。Java 功能预览是版本中引入的新功能,但默认情况下处于禁用状态。它们包含在新版本中只是为了收集开发者社区的反馈,因此它们可能仍然会发生变化。这就是为什么不建议在生产代码中使用它们的原因。要在编译时启用它们,您需要执行以下操作:
javac --enable-preview --release 14
如果您想在运行时启用它们,您将需要运行以下命令:
java --enable-preview YourClass
当然,您也可以在 IDE 中启用它们,但请注意不要在所有新项目中默认启用预览!让我们看一下在未来的 Java 版本中会对我们的编码产生较大影响的变化。

Java帖子

Java Records 是我们许多人长期以来一直渴望的一个功能。我猜您已经发现自己处于需要为每个现有字段实现toStringhashCodeequals以及 getter 的情况。Kotlin 有数据类来解决这个问题,Java 也打算通过发布Scala 已经以案例类形式提供的记录类来解决这个问题。这些类的主要目的是将不可变数据存储在对象中。让我们举个例子来看看 Java 可以变得多么好。这是我们需要编写多少代码来创建和比较我们的类: Employee
package com.theboreddev.java14;

import java.util.Objects;

public class Employee {
    private final String firstName;
    private final String surname;
    private final int age;
    private final Address address;
    private final double salary;

    public Employee(String firstName, String surname, int age, Address address, double salary) {
        this.firstName = firstName;
        this.surname = surname;
        this.age = age;
        this.address = address;
        this.salary = salary;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getSurname() {
        return surname;
    }

    public int getAge() {
        return age;
    }

    public Address getAddress() {
        return address;
    }

    public double getSalary() {
        return salary;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return age == employee.age &&
                Double.compare(employee.salary, salary) == 0 &&
                Objects.equals(firstName, employee.firstName) &&
                Objects.equals(surname, employee.surname) &&
                Objects.equals(address, employee.address);
    }

    @Override
    public int hashCode() {
        return Objects.hash(firstName, surname, age, address, salary);
    }

    @Override
    public String toString() {
        return "Employee{" +
                "firstName='" + firstName + '\'' +
                ", surname='" + surname + '\'' +
                ", age=" + age +
                ", address=" + address +
                ", salary=" + salary +
                '}';
    }
}
还有Address它包含的对象:
import java.util.Objects;

public class Address {
    private final String firstLine;
    private final String secondLine;
    private final String postCode;

    public Address(String firstLine, String secondLine, String postCode) {
        this.firstLine = firstLine;
        this.secondLine = secondLine;
        this.postCode = postCode;
    }

    public String getFirstLine() {
        return firstLine;
    }

    public String getSecondLine() {
        return secondLine;
    }

    public String getPostCode() {
        return postCode;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return Objects.equals(firstLine, address.firstLine) &&
                Objects.equals(secondLine, address.secondLine) &&
                Objects.equals(postCode, address.postCode);
    }

    @Override
    public int hashCode() {
        return Objects.hash(firstLine, secondLine, postCode);
    }

    @Override
    public String toString() {
        return "Address{" +
                "firstLine='" + firstLine + '\'' +
                ", secondLine='" + secondLine + '\'' +
                ", postCode='" + postCode + '\'' +
                '}';
    }
}
对于如此简单的事情来说,代码可能太多了,对吧?现在让我们看看新的 Java 条目会是什么样子:
public record EmployeeRecord(String firstName, String surname, int age, AddressRecord address, double salary) {
}
我们不要忘记 Address 类:
public record AddressRecord(String firstLine, String secondLine, String postCode) {
}
这和我们之前用这么多代码写的东西是一样的。同意:这太棒了。以及我们将节省的代码量以及编写的方便性!现在让我们看看 new 运算符有什么区别switch

改进的算子switch

Java中的新运算符switch将解决一些老问题,包括一些错误和代码重复。有了新的运营商,switch这个问题将得到解决。为了用一个例子来解释这一点,我们将DayOfTheWeek在 Java 中创建一个枚举:
public enum DayOfTheWeek {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}
之后,我们的switch将告诉我们一周中的哪个位置对应于这一天。让我们首先看看当前如何使用 Java 11 来做到这一点。
final DayOfTheWeek dayOfTheWeek = DayOfTheWeek.THURSDAY;

        int position = 0;

        switch (dayOfTheWeek) {
            case MONDAY:
                position = 1;
                break;
            case TUESDAY:
                position = 2;
                break;
            case WEDNESDAY:
                position = 3;
                break;
            case THURSDAY:
                position = 4;
                break;
            case FRIDAY:
                position = 5;
                break;
            case SATURDAY:
                position = 6;
                break;
            case SUNDAY:
                position = 7;
                break;
        }

        System.out.println("Day " + dayOfTheWeek + " is in position " + position + " of the week");
对于当前的语句,switch我们需要使用一个变量,如果我们错过了一周中的某一天,我们的代码将可以正常编译。这是操作员的问题之一switch:他们太容易出错。那么 Java 14 有何改进呢?我们来看一下:
final DayOfTheWeek dayOfTheWeek = DayOfTheWeek.THURSDAY;

        int position = switch (dayOfTheWeek) {
            case MONDAY -> 1;
            case TUESDAY -> 2;
            case WEDNESDAY -> 3;
            case THURSDAY -> 4;
            case FRIDAY -> 5;
            case SATURDAY -> 6;
            case SUNDAY -> 7;
        };

        System.out.println("Day " + dayOfTheWeek + " is in position " + position + " of the week");
正如您所看到的,新运算符switch可以用作表达式,而不仅仅是用作语句。结果更加简洁和富有表现力。这足以说服我们许多人使用它们,但主要改进之一是,现在switch除非我们涵盖switch. 它会向我们显示这个错误,例如:
Error:(9, 24) java: the switch expression does not cover all possible input values
从现在开始,我们的运算符将无法跳过大小写switch。这与 Kotlin 中的运算符非常相似,您可以在文档when中阅读有关内容。我们还看一下新的文本块。

文本块

您是否曾经抱怨过在 Java 中将JSON blob分配给变量有多么困难?Java 具有多行序列,您可以通过将它们括在三引号中来描述它们。一旦这个功能正式发布,用多行描述长序列将变得更加容易。让我们看看两种模式之间的差异。如果我们想在变量中使用格式化的 JSON,结果会很糟糕:
final String text = "{\"widget\": {\n" +
                "    \"debug\": \"on\",\n" +
                "    \"window\": {\n" +
                "        \"title\": \"Sample Konfabulator Widget\",\n" +
                "        \"name\": \"main_window\",\n" +
                "        \"width\": 500,\n" +
                "        \"height\": 500\n" +
                "    },\n" +
                "    \"image\": { \n" +
                "        \"src\": \"Images/Sun.png\",\n" +
                "        \"name\": \"sun1\",\n" +
                "        \"hOffset\": 250,\n" +
                "        \"vOffset\": 250,\n" +
                "        \"alignment\": \"center\"\n" +
                "    },\n" +
                "    \"text\": {\n" +
                "        \"data\": \"Click Here\",\n" +
                "        \"size\": 36,\n" +
                "        \"style\": \"bold\",\n" +
                "        \"name\": \"text1\",\n" +
                "        \"hOffset\": 250,\n" +
                "        \"vOffset\": 100,\n" +
                "        \"alignment\": \"center\",\n" +
                "        \"onMouseUp\": \"sun1.opacity = (sun1.opacity / 100) * 90;\"\n" +
                "    }\n" +
                "}} ";
另一方面,当新的文本块发布时,一切都会变得简单得多:
final String multiLineText = """
                {"widget": {
                    "debug": "on",
                    "window": {
                        "title": "Sample Konfabulator Widget",
                        "name": "main_window",
                        "width": 500,
                        "height": 500
                    },
                    "image": {\s
                        "src": "Images/Sun.png",
                        "name": "sun1",
                        "hOffset": 250,
                        "vOffset": 250,
                        "alignment": "center"
                    },
                    "text": {
                        "data": "Click Here",
                        "size": 36,
                        "style": "bold",
                        "name": "text1",
                        "hOffset": 250,
                        "vOffset": 100,
                        "alignment": "center",
                        "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
                    }
                }}
                """;
这绝对看起来更好。所有这些都已在 Kotlin 中得到支持,您可以在其类型定义中看到。所以我们看到 Java 从它的竞争对手之一:Kotlin 那里“继承”了许多针对自身问题的解决方案。我们不知道 Oracle 是否及时应对了 Kotlin 的崛起,或者是否来得太晚了。就我个人而言,我相信 Java 正在向前迈出正确的一步,即使这些变化是由其竞争对手以某种方式发起的,并且可能会出现一些延迟。

结论

我认为竞争是 Java 语言有史以来发生过的最好的事情。我的印象是,否则 Java 就会固步自封。此外,Java 的竞争对手已经表明,不同的编程方式是可能的,展示了如何前进并避免过时且低效的代码编写方式。未来的变化将使Java比以往任何时候都更强大,一种适应现代时代的语言,一种想要发展的语言。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION