大人,时代变了!使用 Java 16 或 Kotlin 更好的进行插件或模组开发 - Minecraft(我的世界)中文论坛 - Powered by Discuz!.html

大人,时代变了!使用 Java 16 或 Kotlin 更好的进行插件或模组开发 - Minecraft(我的世界)中文论坛 - Powered by Discuz!

Minecraft(我的世界)中文论坛

标题: 大人,时代变了!使用 Java 16 或 Kotlin 更好的进行插件或模组开发 [打印本页]

作者: 贺兰兰    时间: 2021-7-27 22:37
标题: 大人,时代变了!使用 Java 16 或 Kotlin 更好的进行插件或模组开发
本帖最后由 贺兰兰 于 2021-7-31 10:57 编辑

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

  1. final List<String> list = new ArrayList<>();
复制代码

In Java 16

  1. final var list = new ArrayList<String>();
复制代码

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

In Kotlin

  1. val list = arrayListOf<String>()
复制代码

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

In Java 8

  1. public class Point{

  2.     private int x;
  3.     private int y;

  4.     public Point(int x, int y){
  5.         this.x = x;
  6.         this.y = y;
  7.     }

  8.     public x(){
  9.         return x;
  10.     }

  11.     public y(){
  12.         return y;
  13.     }
  14. }

  15. Point point = new Point(1, 2);
  16. point.x(); // returns 1
  17. point.y(); // returns 2
复制代码

In Java 16

  1. record Point(int x, int y) { }

  2. var point = new Point(1, 2);
  3. point.x(); // returns 1
  4. point.y(); // returns 2
复制代码

In Kotlin

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

  3. var point = Point(1, 2)
  4. point.x // return 1
  5. point.y // return 2
复制代码

增强的 switch

In Java 8

  1. int numLetters;
  2. switch (day) {
  3.     case MONDAY, FRIDAY, SUNDAY:
  4.         numLetters = 6;
  5.         break;
  6.     case TUESDAY:
  7.         numLetters = 7;
  8.         break;
  9.     default:
  10.         String s = day.toString();
  11.         numLetters = s.length();
  12. }
复制代码

In Java 16

  1. int numLetters = switch (day) {
  2.     case MONDAY, FRIDAY, SUNDAY -> 6;
  3.     case TUESDAY                -> 7;
  4.     default      -> {
  5.       String s = day.toString();
  6.       int result = s.length();
  7.       yield result;
  8.     }
  9. };
复制代码

In Kotlin

  1. var numLetters = when (day){
  2.         MONDAY, FRIDAY, SUNDAY -> 6
  3.         TUESDAY -> 7
  4.         else ->{
  5.             var s = day.toString()
  6.               var result = s.length
  7.               result
  8.         }
  9.     }
复制代码

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

In Java 8

  1. // No solution
复制代码

In Java 16

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

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

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

In Kotlin

  1. package pkg.a

  2. sealed class Shape

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


  5. package pkg.b

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

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

文本块

In Java 8

  1. String html = "<html>\n" +
  2.                 "    <body>\n" +
  3.                 "        <p>Hello, world</p>\n" +
  4.                 "    </body>\n" +
  5.                 "</html>\n";
复制代码

In Java 16

  1. String html = """
  2.             <html>
  3.                 <body>
  4.                     <p>Hello, world</p>
  5.                 </body>
  6.             </html>
  7.             """;
复制代码

In Kotlin

  1. var html = """
  2.             <html>
  3.                 <body>
  4.                     <p>Hello, world</p>
  5.                 </body>
  6.             </html>
  7.             """
复制代码

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

In Java 8

  1. a.b.c.i = 99;
  2. ---
  3. Exception in thread "main" java.lang.NullPointerException
复制代码

In Java 16

  1. a.b.c.i = 99;
  2. ---
  3. Exception in thread "main" java.lang.NullPointerException:
  4.       Cannot read field "c" because "a.b" is null
复制代码

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

In Java 8

  1. if (obj instanceof String) {
  2.     String s = (String) obj;
  3.     if(s.length() > 5){
  4.         System.out.println("obj is a String with more than 5 characters: " + s.toUpperCase());
  5.     }
  6. }
复制代码

In Java 16

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

In Kotlin

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

接口中允许私有方法

In Java 8

  1. // Not allowed
复制代码

In Java 16

  1. public interface some {
  2.     private void doSomething(){
  3.         // do something
  4.     }
  5. }
复制代码

In Kotlin

  1. interface some {
  2.     private fun doSomething() {
  3.         // do something
  4.     }
  5. }
复制代码

更方便的将 Stream 收集为 List

In Java 8

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

In Java 16

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

In Kotlin

  1. var result =
  2.   Stream.of("one", "two", "three")
  3.     .filter(s -> s.length() == 3)
  4.     .toList()
复制代码

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

Java 16

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

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

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

  4. "\u2000 hello \u2000".strip(); // will return "hello"
复制代码

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

Java 8

  1. Set<Integer> mySet = new HashSet<>();
  2. mySet.add(1);
  3. mySet.add(2);
  4. mySet.add(3);
  5. List<Integer> myList = new ArrayList<>();
  6. myList.add(1);
  7. myList.add(2);
  8. myList.add(3);
  9. Map<String, Integer> myMap = new HashMap<>();
  10. myMap.put("one",1);
  11. myMap.put("two",2);
复制代码

Java 16

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

Kotlin

  1. var mySet = setOf(1, 2, 3)
  2. var myList = listOf(1, 2, 3)
  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,来体验更高效的开发呢?

(完)

[groupid=1511]Server CT[/groupid]
作者: shuaishuai520    时间: 2021-7-27 22:38
wc,太详细了,楼主给你顶一个
作者: 逍遥先生.    时间: 2021-7-27 22:41
这就支持
作者: 紅葉    时间: 2021-7-27 22:45
建议各位都拥抱新时代的潮流,使用Java16或kotlin
作者: 小六子鸭.    时间: 2021-7-27 23:16
本帖最后由 小六子鸭. 于 2021-7-28 18:16 编辑

前些天还在赶忙着学kotlin呢 这就看到贺兰大大的文章了
作者: Bowser    时间: 2021-7-27 23:27
Nashorn被删了,然而forge又把Nashorn加回来了(笑)
作者: Glom_    时间: 2021-7-30 01:44
Great.
作者: 结冰的离季    时间: 2021-7-30 21:15
kotlin +1 使用kotlin 何不再试试 Gradle Kotlin DSL 来构建
这里是gradle Groovy语言转 gradle Kotlin 的官方迁移教程:链接
以下是我参考文档+自己摸索写的Gradle Kotlin 脚本
build.gradle.kts

  1. plugins {
  2.     java
  3.     id("org.jetbrains.kotlin.jvm") version "1.4.0"
  4.     id("com.github.johnrengelman.shadow") version "6.1.0"   //使用shadow将依赖打包进jar包
  5. //    kotlin("jvm") version "1.0.0"
  6. }
  7. group = "top.iseason.kotlin"          //声明包名
  8. version = "1.0.5"                     //插件版本
  9. val mainClass = "DeEnchantmentPlugin" //插件主类
  10. val author = "Iseason"                //作者
  11. val jarOutputFile = "E:\\mc\\1.17.1 servers\\plugins" //jar导出路径

  12. repositories { //仓库
  13.     mavenCentral()
  14.     maven {
  15.         url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
  16.     }
  17.     maven {
  18.         url = uri("https://repo.codemc.org/repository/maven-public/")
  19.     }
  20. }

  21. dependencies { //依赖
  22.     api("org.spigotmc:spigot-api:1.16.4-R0.1-SNAPSHOT")
  23.     implementation("org.jetbrains.kotlin:kotlin-stdlib:1.4.31")
  24.     implementation("io.github.bananapuncher714:nbteditor:7.17.0")
  25. }

  26. (tasks.getByName("processResources") as ProcessResources).apply {
  27.     val p = "${project.group}.${rootProject.name.toLowerCase()}"
  28.     include("config.yml") //包含config.yml文件

  29.     //包含plugin.yml并修改文件内容,这里我用于同步插件信息
  30.     //plugin.yml里面的例子: name: ${name} \n main: ${main}
  31.     include("plugin.yml").expand(
  32.         "name" to rootProject.name.apply { this.toLowerCase() },
  33.         "main" to "$p.$mainClass",
  34.         "version" to project.version,
  35.         "author" to author
  36.     )
  37. }

  38. //添加ShadowJar任务,将依赖打包,stdlib是kotlin依赖,nbteditor是我自己使用的nbt编辑依赖
  39. tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
  40.     dependencies {
  41.         include(dependency("io.github.bananapuncher714:nbteditor:7.17.0"))
  42.         include(dependency("org.jetbrains.kotlin:kotlin-stdlib:1.4.31"))
  43.     }
  44.     destinationDirectory.set(file(jarOutputFile))
  45. }
  46. //编译完成后的任务
  47. val compileKotlin: org.jetbrains.kotlin.gradle.tasks.KotlinCompile by tasks
  48. compileKotlin.kotlinOptions.jvmTarget = "1.8"
  49. java.sourceCompatibility = JavaVersion.VERSION_1_8
  50. java.targetCompatibility = JavaVersion.VERSION_1_8
  51. //设置jar包输出路径
  52. tasks.jar {
  53.     this.destinationDirectory.set(file(jarOutputFile))
  54. }
复制代码




作者: Dasffafa    时间: 2021-7-31 08:56
本帖最后由 Dasffafa 于 2021-8-1 20:12 编辑

Java16看起来很棒啊,考虑切换了编辑:放弃了这个想法,因为Java16不是长期支持版本,今年九月,长期支持版本Java17就要面世了,到时候我会换Java17

作者: 黎雨轩    时间: 2021-10-17 21:22
所以1.16版本成了Forge团队的LTS,mcp不更了,java版本也改了,不变的是热爱的心
作者: WorriKe    时间: 2022-1-4 23:02
Kotlin加一 真希望能换掉顽固的Java 8...
作者: WorriKe    时间: 2022-1-4 23:29
结冰的离季 发表于 2021-7-30 21:15
kotlin +1 使用kotlin 何不再试试 Gradle Kotlin DSL 来构建
这里是gradle Groovy语言转 gradle Kotlin 的 ...

太感谢了 正好在研究KTS😘
作者: AustinPaul    时间: 2022-1-7 16:04
这个简洁,必须支持。
作者: 心伤丶泪    时间: 2022-2-7 11:23
java16    适合低版本?    java原来好像是可以适用于部分软件程序的
作者: teddyxlandlee    时间: 2022-2-9 14:06
最近发现 Kotlin 1.6.10 编译器针对 Java 8+ 做了亿些优化,高兴了一下午
多图警告




作者: teddyxlandlee    时间: 2022-2-9 14:15
本帖最后由 teddyxlandlee 于 2022-2-9 14:20 编辑
结冰的离季 发表于 2021-7-30 21:15
kotlin +1 使用kotlin 何不再试试 Gradle Kotlin DSL 来构建
这里是gradle Groovy语言转 gradle Kotlin 的 ...

现在一般都不用shadow吧,即使用shadow也要relocate一下,防止跟其它Mod冲突
一般用的是 loom 提供的 dependencies -> include
  1. dependencies {
  2.      // ...
  3.      include (implementation ("com.google.guava:guava:31.0.1-jre")
  4.      modImplementation ("net.fabricmc:fabric-language-kotlin:1.7.0+kotlin.1.6.10")      // 这是 Fabric 给的 Fabric Language Kotlin 模组,自带 LanguageAdapter 和 kotlin-stdlib-jdk8、kotlin-reflect 等基本库。这里不 include 的原因是这玩意在 CurseForge 上有,很多 Mod 也在用,体积也不小,不必 include,只需作为前置让玩家自行下载即可
  5. }
复制代码

你们在用 Forge/Bukkit 啊,没事了
但记得relocate一下
学学人家 Lucky Block 作者是怎么做的


作者: 456413    时间: 2022-2-10 10:52
java16定义语法逐渐向javascript靠拢。。。难道java逐渐从强类型转向弱类型语言了?[哭笑][哭笑][哭笑]
作者: 贺兰兰    时间: 2022-2-10 12:05
456413 发表于 2022-2-10 10:52
java16定义语法逐渐向javascript靠拢。。。难道java逐渐从强类型转向弱类型语言了?[哭笑][哭笑][哭笑] ...

?




欢迎光临 Minecraft(我的世界)中文论坛 (https://www.mcbbs.net/)Powered by Discuz! X3.5