那些年,Kotlin 都截胡了哪些 Java 新特性

2022-11-14 17:43:17 浏览数 (1)

那些年,Kotlin 都截胡了哪些 Java 新特性

众所周知,Kotlin被称为最好的 Java。自 Kotlin 发布以来,凭借着其在 JVM 平台上惊人的兼容性,互操作性以及新特性支持,其迅速成为了广泛使用的 JVM 语言之一,就连 Google 也将 Kotlin 钦定为 Android 的首选开发语言。Kotlin 相对 Java 提供了非常多的特性,这些特性甚至截胡了某些 Java 即将推出的新特性,现在就让我们来盘点一下这些被 Kotlin “截胡” 的 Java 新特性吧…

JEP 286: Local-Variable Type Inference

JEP 286: 本地变量类型推断 在 Java 10 发行,为在局部作用域的具有初始值的变量提供了自动类型推断:

代码语言:javascript复制
var list = new ArrayList<String>();  // infers ArrayList<String>
var stream = list.stream();          // infers Stream<String>

Kotlin 则始终支持(无论在全局还是局部作用域)变量自动类型推断:

代码语言:javascript复制
var list = ArrayList<String>();      // infers ArrayList<String>
val stream = list.stream();          // infers Stream<String>

JEP 325/354/361: Switch Expressions

JEP 361: Switch 表达式 在经过 Java 12 和 13 的两次预览后,终于在 Java 14 中发行,其允许一种增强的 switch 语法:使用 case … -> labels 的方式来表达分支以避免输入额外的 break; 语句;更重要的是,switch 语句支持作为表达式返回一个结果给变量:

代码语言:javascript复制
int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    case THURSDAY, SATURDAY     -> 8;
    case WEDNESDAY              -> 9;
    default -> 0;
};

在 Kotlin 中则通过 when 语句获得同样的体验:

代码语言:javascript复制
var numLetters: Int = when (day){
    MONDAY, FRIDAY, SUNDAY -> 6;
    TUESDAY                -> 7;
    THURSDAY, SATURDAY     -> 8;
    WEDNESDAY              -> 9;
    else -> 0;
}

JEP 355/368/378: Text Blocks

JEP 378: 文本块 在 Java 15 完成预览并最终发行,其提供了一种更好的描述多行字符串的方式,而不是通过添加 n 这样的转义序列表达:

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

在 Kotlin 中,文本块同样被支持:

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

JEP 401: Primitive Classes (Preview)

JEP 401: 原始类(预览) 是一个尚在候选(Candidate)状态的 JEP 提案,其试图允许 Java 开发者创建像基本数据类型那样工作的类以提高包装对象的性能:

代码语言:javascript复制
primitive class Point implements Shape {
    private double x;
    private double y;

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

    public double x() { return x; }
    public double y() { return y; }

    public Point translate(double dx, double dy) {
        return new Point(x dx, y dy);
    }

    public boolean contains(Point p) {
        return equals(p);
    }
}

interface Shape {
    boolean contains(Point p);
}

Kotlin 则在 1.5.0 引入了 value class 的概念,允许为单一基本数据类型创建包装对象:

代码语言:javascript复制
@JvmInline
value class Name(val s: String) {
    init {
        require(s.length > 0) { }
    }

    val length: Int
        get() = s.length

    fun greet() {
        println("Hello, $s")
    }
}

fun main() {
    val name = Name("Kotlin")
    name.greet() // method `greet` is called as a static method
    println(name.length) // property getter is called as a static method
}

尽管此两者的运作方式和限制条件有很大的不同,但其目标事实上是相同的:希望减少包装器对象额外的内存使用,优化内存结构。

JEP 360/397/409: Sealed Classes

JEP 409: 密封类 在 Java 17 完成预览并最终发行,其允许限定哪些类和接口可以继承和实现它们,以此为其它语言特性提供更好的模式匹配推断:

代码语言: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()
};

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) {
    is Circle -> Math.pow(shape.radius(), 2) * Math.PI
    is Rectangle -> shape.a() * shape.b()
}

JEP 425: Virtual Threads (Preview)

JEP 425: 虚拟线程(预览) 在 Java 19 发行,其提供了一套有栈协程系统,以减少系统级调用线程带来的高开销。

Kotlin 提供了 Kotlin 协程以达到近似的目的,不过值得一提的是,Kotlin 协程是无栈协程,这意味着其在互操作性和使用内存大小上相比有栈协程会较差一些。

JEP 428: Structured Concurrency

作为 Project Loom 的一部分,与 JEP 425 一样,JEP 428: 结构化并发(孵化) 同样在 Java 19 发行,其提供了一套更好的 API 来处理多线程下的程序调度:

代码语言:javascript复制
Response handle() throws ExecutionException, InterruptedException {
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        Future<String>  user  = scope.fork(() -> findUser());
        Future<Integer> order = scope.fork(() -> fetchOrder());

        scope.join();           // Join both forks
        scope.throwIfFailed();  // ... and propagate errors

        // Here, both forks have succeeded, so compose their results
        return new Response(user.resultNow(), order.resultNow());
    }
}

同样,Kotlin 协程也带来了类似的调度方式:

代码语言:javascript复制
suspend fun handle() = coroutineScope {
    val deferredOne = async {
        delay(1000)
        "String"
    }
    val deferredTwo = async {
        delay(2000)
        1
    }
    return@coroutineScope Pair(deferredOne.await(), deferredTwo.await())
}

JEP 430: String Templates

JEP 430: 字符串模板 (预览) 是一个尚在候选状态的 JEP 提案,其引入了一种间接方便的在字符串中嵌入表达式的方式:

代码语言:javascript复制
String name = "Joan";
String info = STR."My name is {name}";
assert info.equals("My name is Joan");   // true

Kotlin 提供的字符串模板语法如下:

代码语言:javascript复制
"x plusy equals ${x   y}"

JEP 305/375/394: Pattern Matching for instanceof

JEP 394: instanceof 的模式匹配 在 Java 16 完成预览并最终发行,其引入了一种在指定作用域内使用 instanceof 进行的自动类型推断:

代码语言:javascript复制
if (obj instanceof String s) {
  // obj is cast to String as variable 's'
}

Kotlin 通过其 Smart Cast 机制自动完成类型推断:

代码语言:javascript复制
lateinit var obj: Any
if (obj is String) {
    obj.substring(1) // Smart cast to kotlin.String
}

0 人点赞