Eit*_*s30 4 java enums kotlin sealed-class
我是 Kotlin 新手。我正在读一本书,其中显示了一个密封类作为枚举的“扩展”。我看不出他们之间有什么相似之处。在我看来,密封类与继承更相关,因为每个类都可以继承它并向其添加函数和属性例如:
sealed class messageType
class MessageSuccess (var msg: String) : MwssageType()
class MessageFailure (var msg: String, var e: Exeception) : MwssageType()
Run Code Online (Sandbox Code Playgroud)
我在这里看不到像Enum那样的值,只有继承的问题。有人能解释一下我找不到的 Enum 和 Sealed 之间的想象是什么吗?也许它的威力在于将它与when表达式一起使用?
我认为文档所指的扩展实际上并不是扩展枚举,而是像枚举这样具有更多功能的工具,因为它可以保存状态。让我们看一下您的枚举示例。
\nsealed class SealedMessageType\nclass MessageSuccess (val msg: String) : SealedMessageType()\nclass MessageFailure (val e: Exeception) : SealedMessageType()\n\nenum class EnumMessageType {\n Success,\n Failure\n}\nRun Code Online (Sandbox Code Playgroud)\n现在,如果您使用枚举,您将拥有:
\nval enumMessageType: EnumMessageType = callNetwork()\n\n when(enumMessageType) {\n EnumMessageType.Success -> { TODO() }\n EnumMessageType.Failure -> { TODO() }\n }\nRun Code Online (Sandbox Code Playgroud)\n在这里,当您使用枚举时,您无法从枚举检索结果的数据,并且您需要使用其他变量获取消息或错误。你唯一能得到的是结果的类型,没有它的状态。但对于密封类:
\nval sealedMessageType: SealedMessageType = callNetwork()\n\n when(sealedMessageType) {\n is MessageSuccess -> { println(sealedMessageType.msg) }\n is MessageFailure -> { throw sealedMessageType.e }\n }\nRun Code Online (Sandbox Code Playgroud)\nIDE 可以智能转换您的结果,您可以获得结果的状态(如果成功则显示消息,如果失败则显示异常)。这就是文档的扩展含义。
\n但总的来说,你是对的,密封类是关于继承的。事实上,密封类只不过是一个具有私有构造函数且无法实例化的抽象类。我们看一下反编译后的java代码:
\n@Metadata(\n mv = {1, 4, 0},\n bv = {1, 0, 3},\n k = 1,\n d1 = {"\\u0000\\u0014\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0010\\u0000\\n\\u0000\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0018\\u0002\\n\\u0000\\b6\\u0018\\u00002\\u00020\\u0001B\\u0007\\b\\u0002\xc2\xa2\\u0006\\u0002\\u0010\\u0002\\u0082\\u0001\\u0002\\u0003\\u0004\xc2\xa8\\u0006\\u0005"},\n d2 = {"Lcom/example/customview/SealedMessageType;", "", "()V", "Lcom/example/customview/MessageSuccess;", "Lcom/example/customview/MessageFailure;", "app"}\n)\npublic abstract class SealedMessageType {\n private SealedMessageType() {\n }\n\n // $FF: synthetic method\n public SealedMessageType(DefaultConstructorMarker $constructor_marker) {\n this();\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n@Metadata(\n mv = {1, 4, 0},\n bv = {1, 0, 3},\n k = 1,\n d1 = {"\\u0000\\u0012\\n\\u0002\\u0018\\u0002\\n\\u0002\\u0018\\u0002\\n\\u0000\\n\\u0002\\u0010\\u000e\\n\\u0002\\b\\u0004\\u0018\\u00002\\u00020\\u0001B\\r\\u0012\\u0006\\u0010\\u0002\\u001a\\u00020\\u0003\xc2\xa2\\u0006\\u0002\\u0010\\u0004R\\u0011\\u0010\\u0002\\u001a\\u00020\\u0003\xc2\xa2\\u0006\\b\\n\\u0000\\u001a\\u0004\\b\\u0005\\u0010\\u0006\xc2\xa8\\u0006\\u0007"},\n d2 = {"Lcom/example/customview/MessageSuccess;", "Lcom/example/customview/SealedMessageType;", "msg", "", "(Ljava/lang/String;)V", "getMsg", "()Ljava/lang/String;", "app"}\n)\npublic final class MessageSuccess extends SealedMessageType {\n @NotNull\n private final String msg;\n\n @NotNull\n public final String getMsg() {\n return this.msg;\n }\n\n public MessageSuccess(@NotNull String msg) {\n Intrinsics.checkNotNullParameter(msg, "msg");\n super((DefaultConstructorMarker)null);\n this.msg = msg;\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n在这里你可以看到它SealedMessageType实际上是一个抽象类。抽象类和密封类之间的唯一区别是,编译器会为密封类生成一些元数据,并且当您使用when抽象类无法完成的关键字时,可以警告您缺少的分支。您可以在上面的代码中看到,SealedMessageType类元数据包含MessageSuccess和MessageFailure作为子类,MessageSuccess元数据也包含SealedMessageType作为父类。如果您使用抽象类,则没有此类元数据。
如果您使用这个简单的技巧,如果您错过任何分支,编译器会给您一个错误,并且 IDE 可以帮助您使用Alt+Enter. 诀窍是定义一个详尽的扩展函数:
\nfun main() {\n when(callNetwork()) { // error: when' expression must be exhaustive, add necessary 'is MessageSuccess', 'is MessageFailure' branches or 'else' branch instead\n\n }.exhaustive()\n}\n\nfun Any?.exhaustive() = this\n\n\n\nfun callNetwork(): SealedMessageType {\n TODO()\n}\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
8569 次 |
| 最近记录: |