Enums are great to group objects with similar behavior. They are also efficient because only one instance of them will get created. However, it’s hard to implement enums for classes that has slightly different behaviors. Let me illustrate with an example.
Let’s say we want write a Stats Calculator to compute mathematical statistics for list of values for eg. SUM
, COUNT
, AVG
, QUANTILES
. Let’s first define an interface.
Now we can easily represent different statistics as Enums like this:
We can use it like this:
Easy enough, but it becomes hard once want to support QUANTILE
stat which needs to store percentile to compute in enum instance. For eg. QUANTILE(90).calculate(values)
should compute 90th percentile of values.
AVG
, SUM
, COUNT
.We basically want simple statistics AVG
, SUM
, COUNT
as singletons, but QUANTILE
as dynamically created class for a given percentile value. We have to write lot of boiler plate code to support this difference in behavior, he is one way to do it.
That’s lot of work.
Kotlin’s has powerful sealed classes to easily solve use cases like this.
Let’s first understand what a sealed class is:
Sealed classes are used for representing restricted class hierarchies. They are, in a sense, an extension of enum classes.
A sealed class can have subclasses, but all of them must be declared in the same file. A subclass of a sealed class can have multiple instances which can contain state. You can declare the subclasses inside the sealed class or outside but they always have to be declared in the same file.
A sealed class is abstract by itself, it cannot be instantiated directly and can have abstract members.
Sealed classes are not allowed to have non-private constructors (their constructors are private by default).
Now, we know about Sealed classes, let’s see how we can use sealed classes to implement StatsCalculator
Let’s create a sealed Stats class and sub classes for each Stats like this.
Kotlin supports object declaration to create Singletons in single line. Language itself supports defining exactly what we want:
AVG
, COUNT
, SUM
are made as singleton using object declaration.QUANTILE
is defined as regular class to store percentile value.Stats
.Now, the calculate method can be implemented clear and concise way.
The code is not only concise, the beauty of when expression helps us avoid potentials errors at runtime.
Since when expression is used to directly return value, if we add a new stats say MAX
extending Stats
but forget to update calculate method, compiler will throw error.
when’ expression must be exhaustive, add necessary ‘Max’ branch or ‘else’ branch instead.
This is possible with sealed classes because all subclasses are declared in same file, so compiler will know all possible values.
The idea of Sealed classes isn’t new. The sealed classes allows us to easily work with Algebraic data types. The similar features are available in other languages like
So we can use Kotlin’s sealed classes to solve those problems that requires Algebraic data types.
If you don’t have luxury to use Kotlin, you can check out Spotify’s data enums to do in plain old Java.
Update on 12/03/2018:
The code examples are updated to use Doculet.