Kotlin中的密封类是什么? [英] What are sealed classes in Kotlin?

查看:231
本文介绍了Kotlin中的密封类是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Kotlin的初学者,最近阅读了有关密封类的信息.但是从文档中,我唯一真正想到的就是它们存在.

I'm a beginner in Kotlin and recently read about Sealed Classes. But from the doc the only think I actually get is that they are exist.

该文档指出,它们代表受限的类层次结构".除此之外,我发现他们是具有超能力的枚举.这两个方面实际上都不清楚.

The doc stated, that they are "representing restricted class hierarchies". Besides that I found a statement that they are enums with superpower. Both aspects are actually not clear.

那么您可以帮我解决以下问题:

So can you help me with the following questions:

  • 什么是密封类,使用它们的惯用方式是什么?
  • 这样的概念是否存在于其他语言中,例如Python,Groovy或C#?

更新: 我仔细检查了此博客帖子,但仍然没办法围绕这个概念.如帖子

UPDATE: I carefully checked this blog post and still can't wrap my head around that concept. As stated in the post

好处

此功能使我们可以定义以下类的层次结构: 受其类型(即子类)限制.由于所有子类都需要 在密封类的文件中定义,就没有机会了 编译器不知道的未知子类.

The feature allows us to define class hierarchies that are restricted in their types, i.e. subclasses. Since all subclasses need to be defined inside the file of the sealed class, there’s no chance of unknown subclasses which the compiler doesn’t know about.

为什么编译器不了解其他文件中定义的其他子类?甚至IDE也知道这一点.只需在IDEA中按Ctrl+Alt+B即可,例如,List<>定义,即使在其他源文件中也将显示所有实现.如果可以在某个第三方框架中定义一个子类,而该子类在应用程序中未使用,那么我们为什么要关心它呢?

Why the compiler doesn't know about other subclasses defined in other files? Even IDE knows that. Just press Ctrl+Alt+B in IDEA on, for instance, List<> definition and all implementations will be shown even in other source files. If a subclass can be defined in some third-party framework, which not used in the application, why should we care about that?

推荐答案

假设您有一个域(您的宠物),在该域中您知道类型的确定枚举(计数).例如,您只有两个宠物(您将使用名为MyPet的类对其进行建模).喵喵是你的猫,菲多是你的狗.

Say you have a domain (your pets) where you know there is a definite enumeration (count) of types. For example, you have two and only two pets (which you will model with a class called MyPet). Meowsi is your cat and Fido is your dog.

比较该伪造示例的以下两种实现:

Compare the following two implementations of that contrived example:

sealed class MyPet
class Meowsi : MyPet()
class Fido : MyPet()

因为您使用了密封类,所以当您需要根据宠物的类型执行操作时,MyPet的可能性将一分为二,并且可以确定MyPet实例恰好是以下实例之一这两个选项:

Because you have used sealed classes, when you need to perform an action depending on the type of pet, then the possibilities of MyPet are exhausted in two and you can ascertain that the MyPet instance will be exactly one of the two options:

fun feed(myPet: MyPet): String {
    when(myPet) {
       is Meowsi -> "Giving cat food to Meowsi!"
       is Fido -> "Giving dog biscuit to Fido!" 
    }
}

如果您不使用密封类,则可能性不会一并列举两次,您需要包含一个else语句:

If you don't use sealed classes, the possibilities are not exhausted in two and you need to include an else statement:

open class MyPet
class Meowsi : MyPet()
class Fido : MyPet()

fun feed(myPet: MyPet): String {
    when(myPet) {
       is Mewosi -> "Giving cat food to Meowsi!"
       is Fido -> "Giving dog biscuit to Fido!" 
       else -> "Giving food to someone else!" //else statement required or compiler error here
    }
}

换句话说,没有密封的班级,就不会穷尽(完全覆盖)可能性.

In other words, without sealed classes there is not exhaustion (complete coverage) of possibility.

请注意,您可以使用Java enum来实现所有功能,但是这些不是完全成熟的类.例如,enum不能是另一个类的子类,只能实现一个接口(感谢 EpicPandaForce ).

Note that you could achieve exhaustion of possiblity with Java enum however these are not fully-fledged classes. For example, enum cannot be subclasses of another class, only implement an interface (thanks EpicPandaForce).

完全耗尽可能性的用例是什么?打个比方,想像一下您的预算很紧,您的饲料非常珍贵,并且您想确保自己最终不会喂养不属于家庭的多余宠物.

What is the use case for complete exhaustion of possibilities? To give an analogy, imagine you are on a tight budget and your feed is very precious and you want to ensure you don't end up feeding extra pets that are not part of your household.

没有sealed类,您的家庭/应用程序中的其他人可以定义一个新的MyPet:

Without the sealed class, someone else in your home/application could define a new MyPet:

class TweetiePie : MyPet() //a bird

else语句中包含的这种不想要的宠物将由您的feed方法喂养:

And this unwanted pet would be fed by your feed method as it is included in the else statement:

       else -> "Giving food to someone else!" //feeds any other subclass of MyPet including TweetiePie!

同样,在您的程序中用尽可能性是可取的,因为它减少了您的应用程序可以进入的状态数量,并减少了在行为可能定义不正确的情况下发生错误的可能性.

Likewise, in your program exhaustion of possibility is desirable because it reduces the number of states your application can be in and reduces the possibility of bugs occurring where you have a possible state where behaviour is poorly defined.

因此需要sealed类.

请注意,如果将when用作表达式,则仅会得到强制性的else语句.根据文档:

Note that you only get the mandatory else statement if when is used as an expression. As per the docs:

如果将[when]用作表达式,则满足条件的分支的值将成为整个表达式的值[...],并且else分支是必需的,除非编译器可以证明所有可能的条件案件有分支条件

If [when] is used as an expression, the value of the satisfied branch becomes the value of the overall expression [... and] the else branch is mandatory, unless the compiler can prove that all possible cases are covered with branch conditions

这意味着您将无法获得密封类的好处)

This means you won't get the benefit of sealed classes for something like this):

fun feed(myPet: MyPet): Unit {
    when(myPet) {
        is Meowsi -> println("Giving cat food to Meowsi!") // not an expression so we can forget about Fido
    }
}

要在这种情况下精疲力尽,您需要将语句转换为具有返回类型的表达式.这样的扩展功能会有所帮助:

To get exhaustion for this scenario, you would need to turn the statement into an expression with return type. An extension function like this would help:

val <T> T.exhaustive: T
    get() = this

然后您可以执行以下操作:

Then you can do:

fun feed(myPet: MyPet): Unit {
    when(myPet) {
        is Meowsi -> println("Giving cat food to Meowsi!") 
    }.exhaustive // compiler error because we forgot about Fido
}

有关进一步的说明,请参见后续文章.

See the following article for a further explanation.

这篇关于Kotlin中的密封类是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆