您现在的位置是:亿华云 > IT科技类资讯

6个能让你的Kotlin代码库更有意思的“魔法糖”

亿华云2025-10-03 06:57:12【IT科技类资讯】1人已围观

简介语法糖会导致分号的悲剧。—— Alan J. Perlis我们不断地失去一些东西。其中一些东西相对来说会更重要,现在重新拣起来还不算太晚。Kotlin 语言为程序员的生活带来了大量新的概念和特性,它们

语法糖会导致分号的魔法糖悲剧。—— Alan J. Perlis

我们不断地失去一些东西。代码其中一些东西相对来说会更重要,库更现在重新拣起来还不算太晚。有意Kotlin 语言为程序员的魔法糖生活带来了大量新的概念和特性,它们在日常开发中使用起来会很困难。代码我在生产环境中使用了两年 Kotlin 之后,库更才感受到它带来的有意快乐和满足。这是魔法糖怎么发生的?原因就在那些小小的语法糖中。

我会在本文中与你分析我最喜欢的代码 Kotlin 语法糖,它们是库更在我需要写简洁而鲁棒 Android 应用程序组件时发现的。为了让这篇文章读起来更轻松,有意我把它分成三个部分。魔法糖在这第一部分中,代码你会看到密封类和 when() 控制流函数。库更愉快的开始吧!

拥抱“模式匹配”的密封类

最近我的工作中有机会使用 Swift。我不仅要审核代码,还要将其中一些组件翻译成 Kotlin 实现。我读的代码越多,就越感到惊讶。亿华云最对我来说,最吸引人的特性是枚举。可惜 Kotlin 的枚举并不太灵活,我不得不挖掘合适的替代品: 密封类 。

密封类在编程界并不是什么新鲜玩意儿。事实上,密封类是一个非常知名的语言概念。Kotlin 引入了 sealed 关键字,它可用于类声明,表示对类层次结构的限制。某个值可以是有限类型中的一个,但它不能是其它类型。简单地说,你可以使用密封类来代替枚举,甚至做更多事情。

来看看下面的示例代码。

sealed class Response  data class Success(val body: String): Response()  data class Error(val code: Int, val message: String): Response()  object Timeout: Response() 

乍一看,这些代码除只是声明了一些简单的继承关系,但步步深入,香港云服务器就会提示一个谅人的真相。为 Response 类添加的 sealed 关键字到底起到了什么作用呢?提示这个问题最好的方法是使用 IntelliJ IDEA Kotlin Bytecode 工具。

第一 步。查看 Kotlin 字节码 (Kotlin Bytecode)

第二步。将 Kotlin 字节码反编译成 Java 代码

经过这样非常简单地翻译,你可以看到 Kotlin 代码对应的 Java 代码呈现。

public abstract class Response {     private Response() {     }    // $FF: synthetic method    public Response(DefaultConstructorMarker $constructor_marker) {        this();    } } 

你可能已经猜到了,密封类专们用于继承,所以它们是抽象的。不过他们变得与枚举相似的?在这里,Kotlin 编译器做了大量的工作,让你可以在 when() 函数中将 Response 的子类用作分支。此外,Kotlin 提供了很大的灵活性来允许对密封类的继承结构可以被当作数据声明甚至对象来使用。

fun sugar(response: Response) = when (response) {      is Success -> ...     is Error -> ...     Timeout -> ... } 

它不仅提供了非常彻底的表达式,还提供了自动类型转换,因此你可以在不需要额外的转换的情况下使用 Response 实例。

fun sugar(response: Response) = when (response) {      is Success -> println(response.body)     is Error -> println("${ response.code} ${ response.message}")     Timeout -> println(response.javaClass.simpleName) } 

你能想象一下,站群服务器如果没有一个 sealed 的功能,或者根本没有 Kotlin ,它可能看起来是那么的丑陋和复杂?如果你忘记了 Java 语言的一些特性,请再次使用 IntelliJ IDEA Kotlin Bytecode ,但要坐下来使用 - 这可能会让你晕倒。

public final void sugar(@NotNull Response response) {     Intrinsics.checkParameterIsNotNull(response, "response");    String var3;    if (response instanceof Success) {        var3 = ((Success)response).getBody();       System.out.println(var3);    } else if (response instanceof Error) {        var3 = "" + ((Error)response).getCode() +   + ((Error)response).getMessage();       System.out.println(var3);    } else {        if (!Intrinsics.areEqual(response, Timeout.INSTANCE)) {           throw new NoWhenBranchMatchedException();       }       var3 = response.getClass().getSimpleName();       System.out.println(var3);    } } 

总结一下,我很高兴在这种情况下使用 sealed 关键字,因为它让我以类似于 Swift 的方式塑造我的 Kotlin 代码。

使用 when()函数来排列

由于你已经看到了 when()在 sealed 类中的用法,我决定再分享更多强大的功能。 想象一下,你必须实现一个接受两个 enums 并产生一个不可变状态的函数。

enum class Employee {      DEV_LEAD,     SENIOR_ENGINEER,     REGULAR_ENGINEER,     JUNIOR_ENGINEER } enum class Contract {      PROBATION,     PERMANENT,     CONTRACTOR, } 

enum class Employee 描述了在公司 XYZ 中可以找到的所有角色, enum class Contract 包含所有类型的雇佣合同。 基于这两个 enums ,你应该返回一个正确的 SafariBookAccess 。 而且,你的函数必须产生给定 enum 的所有排列的状态。 第一步,我们来创建状态生成函数的签名。

fun access(employee: Employee,            contract: Contract): SafariBookAccess 

现在是时候定义 SafariBooksAccess 结构体了,因为你已了解 sealed 关键字,这是使用它最适合的时机。封装 SafariBookAccess 并不是必须的,但它是封装不同情景下的 SafariBookAccess 的不同状态的好方式。

sealed class SafariBookAccess  data class Granted(val expirationDate: DateTime) : SafariBookAccess()  data class NotGranted(val error: AssertionError) : SafariBookAccess()  data class Blocked(val message: String) : SafariBookAccess() 

那么隐藏在 access() 函数后面的主要意图是什么?全排列!让我们罗列下。

fun access(employee: Employee,            contract: Contract): SafariBookAccess {      return when (employee) {          SENIOR_ENGINEER -> when (contract) {              PROBATION -> NotGranted(AssertionError("Access not allowed on probation contract."))             PERMANENT -> Granted(DateTime())             CONTRACTOR -> Granted(DateTime())         }         REGULAR_ENGINEER -> when (contract) {              PROBATION -> NotGranted(AssertionError("Access not allowed on probation contract."))             PERMANENT -> Granted(DateTime())             CONTRACTOR -> Blocked("Access blocked for $contract.")         }         JUNIOR_ENGINEER -> when (contract) {              PROBATION -> NotGranted(AssertionError("Access not allowed on probation contract."))             PERMANENT -> Blocked("Access blocked for $contract.")             CONTRACTOR -> Blocked("Access blocked for $contract.")         }         else -> throw AssertionError()     } } 

这个代码很完美,但你能让它更像 Kotlin 吗?当你每天对同事的 PR/MR 进行审查时会有什么建议吗?你可能会写一些这样的评论:

太多 when() 函数。使用 Pair 来避免嵌套。 改变枚举参数的顺序,定义 Pair() 对象来让它更易读。 合并重复的 return。 改为一个表达式函数。 fun access(contract: Contract,            employee: Employee) = when (Pair(contract, employee)) {      Pair(PROBATION, SENIOR_ENGINEER),     Pair(PROBATION, REGULAR_ENGINEER),     Pair(PROBATION, JUNIOR_ENGINEER) -> NotGranted(AssertionError("Access not allowed on probation contract."))     Pair(PERMANENT, SENIOR_ENGINEER),     Pair(PERMANENT, REGULAR_ENGINEER),     Pair(PERMANENT, JUNIOR_ENGINEER),     Pair(CONTRACTOR, SENIOR_ENGINEER) -> Granted(DateTime(1))     Pair(CONTRACTOR, REGULAR_ENGINEER),     Pair(CONTRACTOR, JUNIOR_ENGINEER) -> Blocked("Access for junior contractors is blocked.")     else -> throw AssertionError("Unsupported case of $employee and $contract") } 

现在它看起来更整洁,但 Kotlin 还有语法糖可以完全省略对 Pair 的定义。棒!

fun access(contract: Contract,            employee: Employee) = when (contract to employee) {      PROBATION to SENIOR_ENGINEER,     PROBATION to REGULAR_ENGINEER -> NotGranted(AssertionError("Access not allowed on probation contract."))     PERMANENT to SENIOR_ENGINEER,     PERMANENT to REGULAR_ENGINEER,     PERMANENT to JUNIOR_ENGINEER,     CONTRACTOR to SENIOR_ENGINEER -> Granted(DateTime(1))     CONTRACTOR to REGULAR_ENGINEER,     PROBATION to JUNIOR_ENGINEER,     CONTRACTOR to JUNIOR_ENGINEER -> Blocked("Access for junior contractors is blocked.")     else -> throw AssertionError("Unsupported case of $employee and $contract") } 

这个结构让我的生活变得轻松,也让 Kotlin 代码读写变得容易,我希望你也觉得这很有用。但它是不是不能用于三元组呢?答案是肯定的。

Triple(enum1, enum2, enum3) == enum1 to enum2 to enum3 

以上就是第 1 部分的全部内容,如果你仍然很有兴趣,请继续阅读第 2 部分。干杯!

很赞哦!(4323)