This is part of a series of articles. You can find the rest here.

For better or worse, we still require inheritance when developing with Kotlin. However, things are a bit different, mostly for the better. Let’s have a look at what tools do we have in the language to solve classical problems in Object Modelling.

The final modifier

In Java, inheritance is available by default. Meaning that when declaring a new type we can define children types that inherit their behaviour and data model. If we want to make sure that others can not extend our types we have to provide a final modifier to them explicitly.

However, why would we want to do something like this? Let’s have a look at what Item 17 from Effective Java has to say:

Item 17: Design and document for inheritance or else prohibit it

In a few words, when allowing for inheritance, we have to make sure we are clear on how the base type behaves and how child types are expected to behave. Either by providing accurate and descriptive names, or by giving adequate documentation. Clarity is required as both sides may depend on each other’s behaviours, i.e. the order of invocation.

Another alternative is to avoid inheritance altogether. Making our type more deterministic and predictable as its behaviour is not going to change if a child type decides to change the way it works. Moreover, when we use them, we can be assured that we get what we expected unlike what happens in the next example.

‘Final’ example

Let’s say we have a component which takes a currency, and an amount, returning a String representation.

Now, someone (probably ourselves) comes along some time later and decides our program needs a formatter that also rounds up the amount.

Here, we’ve added a new behaviour, and since we can still cast it to the original type of PriceFormatter whoever receives an instance is not going to know we had that behaviour changed necessarily.

To make sure this doesn’t happen we can declare the type as final , consequently forcing future implementers to have to create a bespoke formatter. Incidentally, making the code more explicit.

Kotlin has no finals

I lied there. What it doesn’t have is the final keyword. That is because types are final out of the box. Most of the time we declare types that don’t require inheritance. So, in Kotlin if we want something to have children, we have to declare it explicitly. There are a few ways: open or abstract , and sealed . Let’s have a look at the first two.

These two may look similar and, as they are, we can say they are effectively the same. Both allow other types to inherit from them. The main difference so far is that we can instantiate open classes, while the abstract one requires another type to extend it.

Functions defined on either of them are also final by default so, in both cases, we have to be explicit regarding on which functions descendents are allowed to override.

It’s important to note that overriding a function in Kotlin requires to use the override keyword. It’s not an annotation as it was in Java. Also, it’s compulsory. No chance there to leave a rogue function after a refactor because we forgot to annotate it before.

Now, in the case of abstract classes, we have another way to allow for function ‘override’. We can declare functions as abstract without a body. Similar to what happens in Java, we are stating that the parent type doesn’t have a full implementation. It is the job of the inheritor to define such logic. In this case, we are forcing to override the given function.

As mentioned before, we can’t instantiate abstract classes. Banning such extension means that, unless we ‘reopen’ the potential for inheritance further, we can’t inherit from any of the types.

All this said, opening the hierarchy of a class should be done on rare occasions. As we’ll see, we have a better tool for this in our Kotlin arsenal.

Closed hierarchies

A closed hierarchy is one that doesn’t allow for extension. All the possible descending types are already declared. In a way, every class declared in Kotlin is a closed hierarchy of size one. However, we can also increase the family tree when needed. We’ve seen how to do this with open and abstract classes.

However, using abstract or open leaves the parent available to be implemented by anybody which can be almost appropriate when building an API or SDK, but never when building a system where we have a finite number of possibilities.

Closed hierarchies are not a feature restricted to Kotlin though. Enumerations, a.k.a.: enums are classes in a perfect disguise which create a closed hierarchy. They behave like other classes, in the sense that they can have methods, fields and even constructors. However, they have some unique properties.

Enums

In Java, the system instantiates enum at the static scope, often when they are first mentioned (depending on the VM). Also in Java, enums cannot extend from other classes. Mainly, because they already extend from the Enum type. They can, however, implement interfaces.

With Kotlin, on the other hand, enums are closer to classes, to the point that we declare them using the enum modifier before the class definition.