语法糖导致分号悲剧。-AlanJ.Perlis我们不断失去东西。其中一些东西会相对比较重要,再去捡也不迟。Kotlin语言给程序员的生活带来了很多新的概念和特性,这些在日常开发中可能很难使用。我花了两年时间在生产中使用Kotlin才能感受到它带来的快乐和满足感。这怎么发生的?原因在于那个小语法糖。在本文中,我将与您一起分析我最喜欢的Kotlin语法糖,它是我在需要编写干净、健壮的Android应用程序组件时发现的。为了使这篇文章更容易阅读,我将其分为三个部分。在第一部分中,您将看到密封类和when()控制流函数。快乐的开始!为“模式匹配”拥抱密封类我最近有机会在工作中使用Swift。我不仅审查代码,而且还将其中一些组件翻译成Kotlin实现。我越读代码,就越惊讶。对我来说,最吸引人的功能是枚举。可惜Kotlin的枚举不是很灵活,我不得不寻找一个合适的替代品:密封类。密封类在编程世界中并不是什么新鲜事。事实上,密封类是一个非常有名的语言概念。Kotlin引入了sealed关键字,可以在类声明中使用它来表达对类层次结构的限制。值可以是有限数量的类型之一,但不能是另一种。简而言之,您可以使用密封类代替枚举,甚至做更多的事情。看看下面的示例代码。sealedclassResponsedataclassSuccess(valbody:String):Response()dataclassError(valcode:Int,valmessage:String):Response()objectTimeout:Response()乍看之下,这些代码只是声明了一些简单的继承关系,但是一步一步,就会揭示一个令人尴尬的事实Response类中添加的sealed关键字到底有什么作用呢?提示这个问题的最佳方法是使用IntelliJIDEAKotlin字节码工具。第一步。查看第二步的Kotlin字节码。将Kotlin字节码反编译成Java代码经过这样非常简单的翻译,就可以看到Kotlin代码对应的Java代码渲染了。publicabstractclassResponse{privateResponse(){}//$FF:syntheticmethodpublicResponse(DefaultConstructorMarker$constructor_marker){this();}}你可能已经猜到了,密封类是为继承而设计的,所以它们是抽象的。但是它们会变得类似于枚举吗?在这里,Kotlin编译器做了很多工作,允许您将Response子类化为when()函数中的一个分支。此外,Kotlin提供了很大的灵活性,允许将密封类的继承结构用作数据声明甚至对象。funsugar(response:Response)=when(response){isSuccess->...isError->...Timeout->...}不仅提供了非常透彻的表达,还提供了自动类型转换,让你的Response无需额外转换即可使用实例。funsugar(response:Response)=when(response){isSuccess->println(response.body)isError->println("${response.code}${response.message}")Timeout->println(response.javaClass.simpleName)}你能想象如果没有密封功能,或者根本没有Kotlin,它会看起来多么丑陋和复杂吗?如果你忘记了Java语言的一些特性,请再次使用IntelliJIDEAKotlinBytecode,但是要使用sittingdown——这可能会让你晕倒。publicfinalvoidsugar(@NotNullResponseresponse){Intrinsics.checkParameterIsNotNull(response,"response");Stringvar3;if(responseinstanceofSuccess){var3=((Success)response).getBody();System.out.println(var3);}elseif(responseinstanceofError){var3=""+((Error)response).getCode()+''+((Error)response).getMessage();System.out.println(var3);}else{if(!Intrinsics.areEqual(response,Timeout.INSTANCE)){thrownewNoWhenBranchMatchedException();}var3=response.getClass().getSimpleName();System.out.println(var3);}}总而言之,我很乐意在此使用sealedcase关键字,因为它可以让我以类似Swift的方式塑造我的Kotlin代码。使用when()函数来排队既然你已经看到了when()在密封类中的使用,我决定分享一些更强大的功能。想象一下,您必须实现一个接受两个枚举并生成不可变状态的函数。enumclassEmployee{DEV_LEAD,SENIOR_ENGINEER,REGULAR_ENGINEER,JUNIOR_ENGINEER}enumclassContract{PROBATION,PERMANENT,CONTRACTOR,}enumclassEmployee描述了XYZ公司可以找到的所有角色,enumclassContract包含了所有类型的雇佣合同。基于这两个枚举,您应该返回正确的SafariBookAccess。此外,您的函数必须生成给定枚举的所有排列的状态。作为第一步,让我们创建状态生成函数的签名。funaccess(employee:Employee,contract:Contract):SafariBookAccess现在是定义SafariBooksAccess结构的时候了,因为您已经知道sealed关键字,这是使用它的最佳时机。包装SafariBookAccess不是必须的,但它是在不同情况下封装SafariBookAccess不同状态的好方法。sealedclassSafariBookAccessdataclassGranted(valexpirationDate:DateTime):SafariBookAccess()dataclassNotGranted(valerror:AssertionError):SafariBookAccess()dataclassBlocked(valmessage:String):SafariBookAccess()那么隐藏在access()函数背后的主要目的是什么?完整列表!让我们列出来。funaccess(员工:员工,合同:合同):SafariBookAccess{返回时(员工){SENIOR_ENGINEER->when(合同){PROBATION->NotGranted(AssertionError(“Accessnotallowedonprobationcontract.”))PERMANENT->Granted(DateTime())承包商->授予(DateTime())}REGULAR_ENGINEER->when(合同){PROBATION->NotGranted(AssertionError(“Accessnotallowedonprobationcontract。”))PERMANENT->Granted(DateTime())CONTRACTOR->Blocked(“Accessblockedfor$contract。”)}JUNIOR_ENGINEER->when(contract){PROBATION->NotGranted(AssertionError("Accessnotallowedonprobationcontract."))PERMANENT->Blocked("Accessblockedfor$contract.")CONTRACTOR->Blocked("Accessnotallowedonprobationcontract.")}else->throwAssertionError()}}这段代码很完美,但是你能不能让它更像Kotlin?你在日常评审同事的PR/MR时有什么建议吗?你可能会写一些这样的评论:太多的when()函数。使用Pair避免嵌套。更改了枚举参数的顺序并定义了Pair()对象以使其更具可读性。合并重复的回报。改为表达式函数。funaccess(contract:Contract,employee:Employee)=when(Pair(contract,employee)){Pair(PROBATION,SENIOR_ENGINEER),Pair(PROBATION,REGULAR_ENGINEER),Pair(PROBATION,JUNIOR_ENGINEER)->NotGranted(AssertionError("Accessnotallowedonprobationcontract."))对(PERMANENT,SENIOR_ENGINEER),对(PERMANENT,REGULAR_ENGINEER),对(PERMANENT,JUNIOR_ENGINEER),对(CONTRACTOR,SENIOR_ENGINEER)->Granted(DateTime(1))对(CONTRACTOR,REGULAR_ENGINEER),对(CONTRACTOR,JUNIOR_ENGINEER)->Blocked("Accessforjuniorcontractorsisblocked.")else->throwAssertionError("Unsupportedcaseof$employeeand$contract")}现在看起来更简洁了,但是Kotlin也有语法糖来完全省略Pair的定义。棒!,PROBATIONtoJUNIOR_ENGINEER,CONTRACTORtoJUNIOR_ENGINEER->Blocked("Accessforjuniorcontractorsisblocked.")else->throwAssertionError("Unsupportedcaseof$employeeand$contract")}这个结构让我的生活变得轻松,让阅读和编写Kotlin代码变得容易,我希望你也发现这个有用.但是不可能使用三元组吗?答案是肯定的。Triple(enum1,enum2,enum3)==enum1toenum2toenum3第1部分就这些了,如果您还有兴趣,请继续阅读第2部分。干杯!
