大人,时代变了!使用 Java 16 或 Kotlin 更好的进行插件或模组开发

发布于 2021-07-27  13 次阅读


大人,时代变了!使用 Java 16 或 Kotlin 更好的进行插件或模组开发

声明:本文章中 Java 8 至 Java 16 以来变化的内容整理自 这个网站,您可以访问该网站以了解更多新版本 Java 的更改

简洁起见,对于某些不重要,或者对开发意义不大的更新,本文并未列出

前言

从很久很久以前,Minecraft 的社区开发者们就开始使用包含了全新的 Stream 库和 Lambda 语句的 Java 8 进行插件或者模组开发,时至今日,Java 8 已成为开发者、服主、玩家使用最多的 Java 版本 —— 或许仍将持续下去,至少对那些忠于旧版本的人们来说。但对于勇于探索新生的冒险者们来说,他们不得不开始正视一个新的拦路虎,亦或者说一种新的机遇 —— 那就是 Java 16。

从 Java Edition 1.17(正确的来说,是 21w19a)开始,Minecraft 需要 Java 16 或更新版本才能运行。对于这个最新更改,人们别无选择,只能慢慢接受 —— 对于玩家和服主来说,可能只是卸载一个旧版本,安装一个新版本的事情。但对于开发者来说,很显然我们需要知道更多。

本文的存在就是这个意义,我们将介绍从 Java 8 开始到 Java 16 重要的开发内容更新,并附带这些更新在以 Java 8 为运行时的 Kotlin 是如何处理的,以帮助开发者们能够更快的适应和享受新的 Java 或者 Kotlin 带来的更高的开发效率。

什么样的开发者适合切换到 Java 16

对于 Minecraft 开发者而言,由于兼容性,很显然并不是所有的开发者都能够切换到 Java 16 进行开发。以普遍理性而言,这些开发者应当可以切换到 Java 16 进行开发:

  • 所有面向 Minecraft 1.17 或更高版本进行开发的模组/插件开发者
  • 面向 Minecraft 1.13+ 的 Bukkit 插件开发者

为什么使用 Kotlin

Kotlin(JVM) 作为一个基于 JVM 平台的开发语言,为开发者们提供了更加舒适的开发方式,收到了很多开发者的追捧。对于 Kotlin 来说,由于其可以基于 Java 8 运行,因此在大多数情况下无需进行更多更改,只需要在模组或插件内包含一个 Kotlin 的标准库,便可以享受 Kotlin 带来的便捷开发。

本文关于 Kotlin 的示例基于 Java 8 运行时,这意味着,某些 JVM 平台更新可能已经在 Kotlin 同样可用,比如 Kotlin 已经添加了对 JVM 中 Record Class 的支持,但我们并不使用这些版本的代码,而将仍旧选择基于 Java 8 运行时时的解决方案 —— 当然,基于更高版本 Java 运行时的 Kotlin 仍旧可以支持这些代码。

请注意,本文章的主题并不是 Kotlin,因此 Kotlin 内容仅作为对比,并非主要内容。

正文:Java 16 到底带来了什么更改?

使用 var 更简洁的创建局部变量

In Java 8

final List<String> list = new ArrayList<>();

In Java 16

final var list = new ArrayList<String>();

注意,var 仅支持局部变量,而不支持全局变量。

In Kotlin

val list = arrayListOf<String>()

使用 Record Class 更方便的创建数据传输对象

In Java 8

public class Point{

    private int x;
    private int y;

    public Point(int x, int y){
        this.x = x;
        this.y = y;
    }

    public x(){
        return x;
    }

    public y(){
        return y;
    }
}

Point point = new Point(1, 2);
point.x(); // returns 1
point.y(); // returns 2

In Java 16

record Point(int x, int y) { }

var point = new Point(1, 2);
point.x(); // returns 1
point.y(); // returns 2

In Kotlin

// With additional toString, hashCode function,like @Data in Lombok
data class Point(val x : Integer,val y : Integer)

var point = Point(1, 2)
point.x // return 1
point.y // return 2

增强的 switch

In Java 8

int numLetters;
switch (day) {
    case MONDAY, FRIDAY, SUNDAY:
        numLetters = 6;
        break;
    case TUESDAY:
        numLetters = 7;
        break;
    default:
        String s = day.toString();
        numLetters = s.length();
}

In Java 16

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    default      -> {
      String s = day.toString();
      int result = s.length();
      yield result;
    }
};

In Kotlin

var numLetters = when (day){
        MONDAY, FRIDAY, SUNDAY -> 6
        TUESDAY -> 7
        else ->{
            var s = day.toString()
            var result = s.length
            result
        }
    }

使用密封类以限定继承的子类

In Java 8

// No solution

In Java 16

public abstract sealed class Shape
    permits Circle, Rectangle {...}

public class Circle extends Shape {...} // OK
public class Rectangle extends Shape {...} // OK
public class Triangle extends Shape {...} // Compile error

// No need for default case if all permitted types are covered
double area = switch (shape) {
    case Circle c    -> Math.pow(c.radius(), 2) * Math.PI
    case Rectangle r -> r.a() * r.b()
};

In Kotlin

package pkg.a

sealed class Shape

class Circle : Shape() {...} // OK
class Rectangle : Shape() {...} // OK


package pkg.b

class Triangle : Shape() {...} // Compile error

// No need for default case if all permitted types are covered
var area = when (shape) {
    is Circle -> Math.pow(shape.radius(), 2) * Math.PI
    is Rectangle -> shape.a() * shape.b()
}

文本块

In Java 8

String html = "<html>\n" +
                "    <body>\n" +
                "        <p>Hello, world</p>\n" +
                "    </body>\n" +
                "</html>\n";

In Java 16

String html = """
            <html>
                <body>
                    <p>Hello, world</p>
                </body>
            </html>
            """;

In Kotlin

var html = """
            <html>
                <body>
                    <p>Hello, world</p>
                </body>
            </html>
            """

当变量为 null 时提供更加详细友好的 NullPointerException 描述

In Java 8

a.b.c.i = 99;
---
Exception in thread "main" java.lang.NullPointerException

In Java 16

a.b.c.i = 99;
---
Exception in thread "main" java.lang.NullPointerException:
      Cannot read field "c" because "a.b" is null

模式匹配 instanceof 以省略必要的显式类型转换

In Java 8

if (obj instanceof String) {
    String s = (String) obj;
    if(s.length() > 5){
        System.out.println("obj is a String with more than 5 characters: " + s.toUpperCase());
    }
}

In Java 16

if (obj instanceof String s && s.length() > 5) {
    System.out.println("obj is a String with more than 5 characters: " + s.toUpperCase());
}

In Kotlin

if (obj is String && obj.length > 5) {
    println("obj is a String with more than 5 characters: " + obj.uppercase(Locale.getDefault()))
}

接口中允许私有方法

In Java 8

// Not allowed

In Java 16

public interface some {
    private void doSomething(){
        // do something
    }
}

In Kotlin

interface some {
    private fun doSomething() {
        // do something
    }
}

更方便的将 Stream 收集为 List

In Java 8

List<String> result =
  Stream.of("one", "two", "three")
    .filter(s -> s.length() == 3)
    .collect(Collectors.toList());

In Java 16

List<String> result =
  Stream.of("one", "two", "three").stream()
    .filter(s -> s.length() == 3)
    .toList();

In Kotlin

var result =
  Stream.of("one", "two", "three")
    .filter(s -> s.length() == 3)
    .toList()

为 String 添加了更多有用的方法

In Java 16

"           ".isBlank(); // will return true

Stream<String> lines = "1\n2\n3\n4".lines(); // will return Stream ["1", "2", "3", "4"]

"a".repeat(4); // will return "aaaa"

"\u2000 hello \u2000".strip(); // will return "hello"

为集合添加了更多易于使用的工厂方法

In Java 8

Set<Integer> mySet = new HashSet<>();
mySet.add(1);
mySet.add(2);
mySet.add(3);
List<Integer> myList = new ArrayList<>();
myList.add(1);
myList.add(2);
myList.add(3);
Map<String, Integer> myMap = new HashMap<>();
myMap.put("one",1);
myMap.put("two",2);

In Java 16

Set<Integer> mySet = Set.of(1, 2, 3);
List<Integer> myList = List.of(1, 2, 3);
Map<String, Integer> myMap = Map.of("one", 1, "two", 2);

In Kotlin

var mySet = setOf(1, 2, 3)
var myList = listOf(1, 2, 3)
var myMap = mapOf("one" to 1, "two" to 2)

除此之外,支持更多平台,支持 TLS 1.3,全新的 jlink 工具,HTML5 标准的 Javadoc,更强大的 ZGC 等特性都将可以在全新的 Java 16 中体验到。

值得一提的是,Java 8 中内置的 JavaScript 解析器 Nashron ,jjs 工具,Java EE,Unsafe::defineAnonymousClass(),基本数据类型的包装对象的构造函数都在 Java 16 中被移除或是废弃。

最后。为什么不现在就切换到 Java 16,来体验更高效的开发呢?

(完)