Kotlin中Sealedclass和继承原理有什么区别?
What is the difference between Sealed class and inheritance principle in Kotlin?
我是 Kotlin 新手。我正在看书,密封的 class 作为 Enum 的“扩展”显示在那里。我看不出它们之间的相似之处。在我看来,Sealed class 与继承更相关,因为每个 class 都可以从它继承并向其添加功能和属性
例如:
sealed class messageType
class MessageSuccess (var msg: String) : MwssageType()
class MessageFailure (var msg: String, var e: Exeception) : MwssageType()
我没有看到这里值就像我们在Enum中看到的那样,只是扭结的继承。
有人能解释一下我找不到的 Enum 和 Sealed 之间的想象是什么吗?
也许它的强大之处在于将它与 when 表达式一起使用时?
我认为文档中所说的扩展的意思,实际上并不是扩展枚举,而是像枚举这样的工具,因为它可以保持状态而具有更强大的功能。让我们看看你的枚举示例。
sealed class SealedMessageType
class MessageSuccess (val msg: String) : SealedMessageType()
class MessageFailure (val e: Exeception) : SealedMessageType()
enum class EnumMessageType {
Success,
Failure
}
现在,如果您使用枚举,您将拥有:
val enumMessageType: EnumMessageType = callNetwork()
when(enumMessageType) {
EnumMessageType.Success -> { TODO() }
EnumMessageType.Failure -> { TODO() }
}
在这里,当您使用枚举时,您无法从枚举中检索结果数据,您需要使用其他变量获取消息或错误。您唯一可以获得的是没有状态的结果类型。但密封 classes:
val sealedMessageType: SealedMessageType = callNetwork()
when(sealedMessageType) {
is MessageSuccess -> { println(sealedMessageType.msg) }
is MessageFailure -> { throw sealedMessageType.e }
}
IDE 可以智能转换您的结果,您可以获得结果的状态(如果成功则消息,如果失败则异常)。这就是文档扩展的意思。
但总的来说你是对的,密封class是关于继承。事实上,一个密封的 class 只不过是一个抽象的 class ,它有一个私有的构造函数并且不能被实例化。让我们看看反编译的 java 代码:
@Metadata(
mv = {1, 4, 0},
bv = {1, 0, 3},
k = 1,
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¢\u0006\u0002\u0010\u0002\u0082\u0001\u0002\u0003\u0004¨\u0006\u0005"},
d2 = {"Lcom/example/customview/SealedMessageType;", "", "()V", "Lcom/example/customview/MessageSuccess;", "Lcom/example/customview/MessageFailure;", "app"}
)
public abstract class SealedMessageType {
private SealedMessageType() {
}
// $FF: synthetic method
public SealedMessageType(DefaultConstructorMarker $constructor_marker) {
this();
}
}
@Metadata(
mv = {1, 4, 0},
bv = {1, 0, 3},
k = 1,
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¢\u0006\u0002\u0010\u0004R\u0011\u0010\u0002\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\u0005\u0010\u0006¨\u0006\u0007"},
d2 = {"Lcom/example/customview/MessageSuccess;", "Lcom/example/customview/SealedMessageType;", "msg", "", "(Ljava/lang/String;)V", "getMsg", "()Ljava/lang/String;", "app"}
)
public final class MessageSuccess extends SealedMessageType {
@NotNull
private final String msg;
@NotNull
public final String getMsg() {
return this.msg;
}
public MessageSuccess(@NotNull String msg) {
Intrinsics.checkNotNullParameter(msg, "msg");
super((DefaultConstructorMarker)null);
this.msg = msg;
}
}
在这里你可以看到 SealedMessageType
实际上是一个抽象 class。 abstract class 和 sealed class 之间的唯一区别是编译器为 sealed classes 生成一些元数据,并且可以在您使用 [=17= 时警告您缺少分支] 关键字无法使用抽象 classes 完成。您可以在上面的代码中看到 SealedMessageType
class 元数据同时包含 MessageSuccess
和 MessageFailure
作为子 classes,并且 MessageSuccess
元数据也包含包含 SealedMessageType
作为父项。如果您使用抽象 classes.
,则没有此类元数据
如果你使用这个简单的技巧,如果你遗漏任何分支,编译器会给你一个错误,IDE 可以帮助你使用 Alt+Enter
实现遗漏的分支。诀窍是定义一个详尽的扩展函数:
fun main() {
when(callNetwork()) { // error: when' expression must be exhaustive, add necessary 'is MessageSuccess', 'is MessageFailure' branches or 'else' branch instead
}.exhaustive()
}
fun Any?.exhaustive() = this
fun callNetwork(): SealedMessageType {
TODO()
}
我是 Kotlin 新手。我正在看书,密封的 class 作为 Enum 的“扩展”显示在那里。我看不出它们之间的相似之处。在我看来,Sealed class 与继承更相关,因为每个 class 都可以从它继承并向其添加功能和属性 例如:
sealed class messageType
class MessageSuccess (var msg: String) : MwssageType()
class MessageFailure (var msg: String, var e: Exeception) : MwssageType()
我没有看到这里值就像我们在Enum中看到的那样,只是扭结的继承。 有人能解释一下我找不到的 Enum 和 Sealed 之间的想象是什么吗? 也许它的强大之处在于将它与 when 表达式一起使用时?
我认为文档中所说的扩展的意思,实际上并不是扩展枚举,而是像枚举这样的工具,因为它可以保持状态而具有更强大的功能。让我们看看你的枚举示例。
sealed class SealedMessageType
class MessageSuccess (val msg: String) : SealedMessageType()
class MessageFailure (val e: Exeception) : SealedMessageType()
enum class EnumMessageType {
Success,
Failure
}
现在,如果您使用枚举,您将拥有:
val enumMessageType: EnumMessageType = callNetwork()
when(enumMessageType) {
EnumMessageType.Success -> { TODO() }
EnumMessageType.Failure -> { TODO() }
}
在这里,当您使用枚举时,您无法从枚举中检索结果数据,您需要使用其他变量获取消息或错误。您唯一可以获得的是没有状态的结果类型。但密封 classes:
val sealedMessageType: SealedMessageType = callNetwork()
when(sealedMessageType) {
is MessageSuccess -> { println(sealedMessageType.msg) }
is MessageFailure -> { throw sealedMessageType.e }
}
IDE 可以智能转换您的结果,您可以获得结果的状态(如果成功则消息,如果失败则异常)。这就是文档扩展的意思。
但总的来说你是对的,密封class是关于继承。事实上,一个密封的 class 只不过是一个抽象的 class ,它有一个私有的构造函数并且不能被实例化。让我们看看反编译的 java 代码:
@Metadata(
mv = {1, 4, 0},
bv = {1, 0, 3},
k = 1,
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¢\u0006\u0002\u0010\u0002\u0082\u0001\u0002\u0003\u0004¨\u0006\u0005"},
d2 = {"Lcom/example/customview/SealedMessageType;", "", "()V", "Lcom/example/customview/MessageSuccess;", "Lcom/example/customview/MessageFailure;", "app"}
)
public abstract class SealedMessageType {
private SealedMessageType() {
}
// $FF: synthetic method
public SealedMessageType(DefaultConstructorMarker $constructor_marker) {
this();
}
}
@Metadata(
mv = {1, 4, 0},
bv = {1, 0, 3},
k = 1,
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¢\u0006\u0002\u0010\u0004R\u0011\u0010\u0002\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\u0005\u0010\u0006¨\u0006\u0007"},
d2 = {"Lcom/example/customview/MessageSuccess;", "Lcom/example/customview/SealedMessageType;", "msg", "", "(Ljava/lang/String;)V", "getMsg", "()Ljava/lang/String;", "app"}
)
public final class MessageSuccess extends SealedMessageType {
@NotNull
private final String msg;
@NotNull
public final String getMsg() {
return this.msg;
}
public MessageSuccess(@NotNull String msg) {
Intrinsics.checkNotNullParameter(msg, "msg");
super((DefaultConstructorMarker)null);
this.msg = msg;
}
}
在这里你可以看到 SealedMessageType
实际上是一个抽象 class。 abstract class 和 sealed class 之间的唯一区别是编译器为 sealed classes 生成一些元数据,并且可以在您使用 [=17= 时警告您缺少分支] 关键字无法使用抽象 classes 完成。您可以在上面的代码中看到 SealedMessageType
class 元数据同时包含 MessageSuccess
和 MessageFailure
作为子 classes,并且 MessageSuccess
元数据也包含包含 SealedMessageType
作为父项。如果您使用抽象 classes.
如果你使用这个简单的技巧,如果你遗漏任何分支,编译器会给你一个错误,IDE 可以帮助你使用 Alt+Enter
实现遗漏的分支。诀窍是定义一个详尽的扩展函数:
fun main() {
when(callNetwork()) { // error: when' expression must be exhaustive, add necessary 'is MessageSuccess', 'is MessageFailure' branches or 'else' branch instead
}.exhaustive()
}
fun Any?.exhaustive() = this
fun callNetwork(): SealedMessageType {
TODO()
}