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

本文最后更新于 450 天前,其中的信息可能已经有所发展或是发生改变。

大人,时代变了!使用 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

代码语言:javascript
复制
final List<String> list = new ArrayList<>();

In Java 16

代码语言:javascript
复制
final var list = new ArrayList<String>();

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

In Kotlin

代码语言:javascript
复制
val list = arrayListOf<String>()

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

In Java 8

代码语言:javascript
复制
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

代码语言:javascript
复制
record Point(int x, int y) { }

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

In Kotlin

代码语言:javascript
复制
// 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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
var numLetters = when (day){
MONDAY, FRIDAY, SUNDAY -> 6
TUESDAY -> 7
else ->{
var s = day.toString()
var result = s.length
result
}
}

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

In Java 8

代码语言:javascript
复制
// No solution

In Java 16

代码语言:javascript
复制
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

代码语言:javascript
复制
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)

文本块

In Java 8

代码语言:javascript
复制
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";

In Java 16

代码语言:javascript
复制
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";

In Kotlin

代码语言:javascript
复制
var html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
"""

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

In Java 8

代码语言:javascript
复制
a.b.c.i = 99;

Exception in thread "main" java.lang.NullPointerException

In Java 16

代码语言:javascript
复制
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

代码语言:javascript
复制
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

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

In Kotlin

代码语言:javascript
复制
if (obj is String && obj.length > 5) {
println("obj is a String with more than 5 characters: " + obj.uppercase(Locale.getDefault()))
}

接口中允许私有方法

In Java 8

代码语言:javascript
复制
// Not allowed

In Java 16

代码语言:javascript
复制
public interface some {
private void doSomething(){
// do something
}
}

In Kotlin

代码语言:javascript
复制
interface some {
private fun doSomething() {
// do something
}
}

更方便的将 Stream 收集为 List

In Java 8

代码语言:javascript
复制
List<String> result =
Stream.of("one", "two", "three")
.filter(s -> s.length() == 3)
.collect(Collectors.toList());

In Java 16

代码语言:javascript
复制
List<String> result =
Stream.of("one", "two", "three").stream()
.filter(s -> s.length() == 3)
.toList();

In Kotlin

代码语言:javascript
复制
var result =
Stream.of("one", "two", "three")
.filter(s -> s.length() == 3)
.toList()

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

In Java 16

代码语言:javascript
复制
"           ".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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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,来体验更高效的开发呢?

(完)