As Kotlin steadily spreads into the masses more and more Java developers are being exposed to “new” concepts which were actually available in other languages for years now. Algebraic Data Types (ADT for short) is one of these concepts.

What are ADT?

Simply spoken, an ADT is a type which is represented by several other subtypes. Still sounds too complicated? Okay. How about this one?

Wait, isn’t this a Java enum? Yes it is. It also happens to be a very simple example of ADT. DeliveryStatus is a type which is indeed represented by several other types (in this case enum entries). But wait, here is more!

Actually, it turns out that when a parcel is dispatched we would also like to know a tracking number.

Here is how an implementation could look like in Java.

Doesn’t look that bad, right? Yet, it is flawed in one important way — trackingNumber is used only if Stage is DISPATCHED and is null in every other case. This is tolerable if the amount of such “special” cases is small, but as code evolves more and more such fields emerge.

Kotlin to the rescue

Kotlin helps us to solve this problem by providing sealed classes. Here is how you would define an ADT in Kotlin.

Let’s see what’s going here.

We define a sealed class. This means that no other class outside of this Kotlin file can extend DeliveryStatus which in turn means that the compiler knows about every possible subtype of DeliveryStatus . We’ll get to that at a later point.

class. This means that no other class outside of this Kotlin file can extend which in turn means that the compiler knows about every possible subtype of . We’ll get to that at a later point. We define some types which extend DeliveryStatus .

. We use an object if there is no point in having different instances of this type.

Notice that now only DeliveryStatus.Dispatching contains a tracking number. No other types have to know about it.

Using ADT

That is all great but how do we use our DeliveryStatus now? One of the ways would be to use when operator which makes working with ADTs especially convenient.

Important things to notice:

There is no need to cast status to Dispatched because the compiler already knows that.

to because the compiler already knows that. We have return even though there is no return type specified. That is because every function returns the Unit by default.

even though there is no return type specified. That is because every function returns the by default. It does not compile.

Wait what? You read it right — it indeed does not compile, but can you guess why?

The reason for compilation error also happen to be another advantage of ADTs. It does not compile because we forgot to specify Delivered status in our when clause. Ouch!

Now it works. ADTs are helping you to ensure that when new types are added you handle them properly. But of course you don’t have to do that and if you’ll remove return from showDeliveryStatus it will compile just fine even without Delivered .

In the end

ADTs are very helpful in cases when you need a single value which represents one of several possible states.

It is easy to get started. Just write your next class using ADT. No need to change anything in the existing code.

ADTs are not a replacement for all model classes, just the ones which have subtypes.

enum is still an ADT.

Let us know what you think in the comments!