如何创建具有不同行为和关联值的灵活枚举?

如何创建具有不同行为和关联值的灵活枚举?

Kotlin密封班的力量

枚举非常适合将具有相似行为的对象组合在一起。 它们也是有效的,因为将仅创建其中一个实例。 但是,很难为行为稍有不同的类实现枚举。 让我举例说明。

示例:统计计算器

假设我们要编写一个统计计算器,以计算数学统计信息,例如用于值列表。 SUMCOUNTAVGQUANTILES 首先定义一个接口。

如何创建具有不同行为和关联值的灵活枚举?

现在,我们可以轻松地将不同的统计信息表示为枚举,如下所示:

如何创建具有不同行为和关联值的灵活枚举?

我们可以这样使用它:

如何创建具有不同行为和关联值的灵活枚举?

足够容易,但是一旦要支持QUANTILE stat变得困难,该QUANTILE需要存储百分位以在枚举实例中进行计算。 例如。 QUANTILE(90).calculate(values)应该计算值的百分之九十

实现具有不同行为的枚举的问题

  • 由于Java枚举必须具有相同的字段,因此我们必须向所有枚举添加百分位数字段,尽管它与AVGSUMCOUNT无关。
  • 我们不知道提前静态创建Quantile实例的百分比值。 因此,它们不再是枚举。
  • 一旦将它们转换为常规类,我们将失去它的默认单例行为。

我们基本上希望将简单的统计数据AVGSUMCOUNT作为单例,但是将QUANTILE作为给定百分位数值的动态创建的类。 我们必须编写很多样板代码来支持这种行为上的差异,他是做到这一点的一种方法。

如何创建具有不同行为和关联值的灵活枚举?

太多的工作。

如何创建具有不同行为和关联值的灵活枚举?

科特林的救援营

Kotlin的强大的密封类可轻松解决此类用例。

首先让我们了解什么是密封类:

密封类用于表示受限类层次结构。 从某种意义上讲,它们是枚举类的扩展。

密封的类可以具有子类,但是所有子类必须在同一文件中声明。 密封类的子类可以具有可以包含状态的多个实例。

您可以在密封类内部或外部声明子类,但始终必须在同一文件中声明子类。
密封类本身是抽象的,不能直接实例化,可以有抽象成员。
密封的类不允许具有非私有的构造函数(默认情况下,它们的构造函数是私有的)。

现在,我们了解了密封的类,让我们看看如何使用密封的类来实现StatsCalculator

让我们为每个Stats这样创建一个密封的Stats类和子类。

如何创建具有不同行为和关联值的灵活枚举?

Kotlin支持对象声明以在单行中创建Singleton。 语言本身支持定义我们想要的:

  • 使用对象声明将简单的统计数据AVGCOUNTSUM设为单例。
  • QUANTILE被定义为用于存储百分位值的常规类。
  • 所有这些类在密封的基类Stats分组在一起。

现在,可以以简洁明了的方式实现计算方法。

如何创建具有不同行为和关联值的灵活枚举?

该代码不仅简洁,表达的时候帮助的美丽我们避免在运行时的潜力错误。

因为当使用表达式直接返回值时,如果我们添加一个新的统计信息,例如说MAX扩展了Stats却忘记了更新计算方法,编译器将抛出错误。

“何时”表达式必须详尽无遗,请添加必要的“最大”分支或“其他”分支。

对于密封类,这是可能的,因为所有子类都在同一个文件中声明,因此编译器将知道所有可能的值。

Sealed类什么时候有用?

密封类的想法不是新的。 密封的类使我们可以轻松地处理代数数据类型 其他语言也提供类似功能,例如

因此,我们可以使用Kotlin的密封类来解决那些需要代数数据类型的问题。

如果您不习惯使用Kotlin,则可以查看Spotify的数据枚举 ,以普通的旧Java进行。

2018年12月3日更新:

代码示例已更新为使用Doculet

From: https://hackernoon.com/how-can-you-create-flexible-enums-with-different-behaviors-and-associated-values-ed42c69be02e