Kotlin 中那些无法复现的 Java 写法

大家吼哇!了解 Kotlin 的朋友们都知道,Kotlin 对 Java 是 100% 兼容的,正如官方所说。

可即便如此,这就代表 Java 中所有的写法就都能在 Kotlin 中复现出来吗?倒也未必。

今天我就来简单盘点一些我发现的那些在 Kotlin 中无法复现出来的 Java 写法。

泛型异常

众所周知,Java 中有一个争议多年的东西:throws 。当它在函数后面声明了若干受检异常后,它便会向外传播,直到被捕获。

在 Java 中,你可以通过如下写法实现一个基于泛型的 throws,以达到让调用者自行决定是否抛出受检异常的目的:

Java 复制代码
public static <T extends Exception> void runAndThrow(int value, Supplier<T> ex) throws T {
    if (value == 0) {
        throw ex.get();
    }
}

可以看到,我定义了一个 runAndThrow,里面包括了一个约束为 Exception 的泛型 T,并且将 T 声明在 throws 处。

此时这个函数是否要求异常受检就取决于 T 的类型了。

Java 复制代码
public static void runThrowException(int value) throws Exception {
    // 受检,因此也要 throws
    runAndThrow(value, Exception::new);
}

public static void runThrowRuntimeException(int value) {
    // runtime 异常不受检,因此不需要 throws
    runAndThrow(value, RuntimeException::new);
}

那么这个写法可以在 Kotlin 中被复现吗?

首先一点需要说明的是,Kotlin 本身并不存在 "受检异常" 这个概念,也因此,实际上 Kotlin 中并没有 throws 这个关键词,

你可以随时抛出一个 Java 中的受检异常而不需要考虑它的传播问题。

Kotlin 复制代码
fun runAndThrow() {
    throw Exception()
}

但是这并不代表在 Kotlin/JVM 中就没有产生 throws 的方法。Kotlin 提供了一个注解 Throws,用来告诉编译器在 JVM 中为它生成 throws。

例如:

Kotlin 复制代码
@Throws(Exception::class)
fun runAndThrow() {
    throw Exception()
}

会被编译为类似下述代码的形式:

Java 复制代码
public void runAndThrow() throws Exception {
    throw Exception();
}

聪明的你可能已经发现问题所在了。既然 Kotlin 将 throws 变成了注解,那么泛型就失去效果了,因为注解中无法指定函数上定义的泛型。

例如下面的写法,是无法通过编译的:

Kotlin 复制代码
@Throws(T::class)
fun <T : Exception> runAndThrow(v: Int, ex: () -> T) {
    if (v == 0) {
        throw ex()
    }
}

枚举内部常量参数

接下来,让我们来看看枚举。首先,假设我们有如下的用于表示性别的枚举:

Java 复制代码
public enum Gender {
    UNKNOWN(0),
    MALE(1),
    FEMALE(2);

    final int value;

    Gender(int value) {
        this.value = value;
    }
}

可以看到,枚举有一个参数 value 用来通过一个数字表示性别。

那么这时候,你想将枚举中的这些参数提取为常量,也便于其他地方使用。

于是你简单做了修改,变成下面的样子:

Java 复制代码
public enum Gender {
    UNKNOWN(Gender.VALUE_UNKNOWN),
    MALE(Gender.VALUE_MALE),
    FEMALE(Gender.VALUE_FEMALE);

    public static final int VALUE_UNKNOWN = 0;
    public static final int VALUE_MALE = 1;
    public static final int VALUE_FEMALE = 2;

    final int value;

    Gender(int value) {
        this.value = value;
    }
}

最终表现没有什么区别,一切正常,按部就班。

那么,如果按照这种写法,在 Kotlin 中呢?

首先是普通的枚举定义:

Kotlin 复制代码
enum class KGender(val value: Int) {
    UNKNOWN(0),
    MALE(1),
    FEMALE(2);
}

看得出来,在 Kotlin 中定义带有参数的枚举更加简单而优雅。不过这不是重点,接下来让我们将这些参数提取为它的内部常量。

我们都知道,在 Kotlin 中想要定义常量需要定义在 伴生对象(companion object) 中。

Kotlin 复制代码
enum class KGender(val value: Int) {
    UNKNOWN(KGender.VALUE_UNKNOWN),
    MALE(KGender.VALUE_MALE),
    FEMALE(KGender.VALUE_FEMALE);

    companion object {
        const val VALUE_UNKNOWN = 0
        const val VALUE_MALE = 1
        const val VALUE_FEMALE = 2
    }
}

OK,大功告成!

...

...

了吗?

如果你尝试这么写,那么你只会得到一个编译错误:

复制代码
e: KExample.kt:3:13 Companion object of enum class 'companion object Companion : Any' is uninitialized here.

它会告诉你,枚举的参数所引用的 Companion ------------ 也就是你的伴生对象 ------------ 尚未初始化。

很明显,对 Kotlin 编译器来讲,对常量的替换原则与 Java 并不相同。

因此,你并不能在一个 Kotlin 枚举中引用一个它内部的常量。老老实实挪出去吧!

结尾

以上就是我目前发现的一些在 Kotlin 中那些无法复现的 Java 写法。当然,无法复现的写法并不能代表 Kotlin 对 Java 的兼容是假的,

毕竟这些刁钻的写法也并不是实现某些功能的唯一解。

撰此文仅为分享,感谢你的阅读,我们下次再见,ヾ( ̄▽ ̄)ByeBye~

相关推荐
一只笨菜鸟6 分钟前
IDEA如何让多个项目同时显示
java·ide·intellij-idea
程序猿乐锅9 分钟前
【从0-1学习Python | 第六篇】 Python 函数:定义、参数与返回值
开发语言·python·学习
G018_star sky♬11 分钟前
java中的字符串常见方法
java·开发语言
熊猫钓鱼>_>12 分钟前
Kotlin Multiplatform for OpenHarmony 实战:kotlinx-serialization JSON 适配
开发语言·华为·kotlin·华为云·json·harmonyos·code
.Bryce.1 小时前
你了解 Blackboard(黑板系统)吗?听说过基于黑板系统实现的智能体吗?
java·网络·gpt·php·agent·codex·黑板系统
银河技术1 小时前
从脚本到 Operator:用 Go 构建可靠的微服务自动扩缩容控制器
开发语言·微服务·golang
GIS数据转换器3 小时前
地下空间综合管廊运营管理平台
大数据·开发语言·人工智能·经验分享·perl
猿究院_夏燊8 小时前
Spring Bean 生命周期
java·后端·spring
Wang's Blog8 小时前
Java 项目实战: 交友项目:登录链路回顾与今日任务地图
java·开发语言·状态模式·交友
信誓旦旦的程序猿9 小时前
【Python量化实战宝典 #08】可转债与债券接口手册:转股价·溢价率·纯债价值(Python 可跑)
java·python·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口