Simulating mixins in Kotlin
The main inspiration for me to write this article is an approach to modeling common parts of Actions in Android applications – I saw often recently – where all common logic (and sometimes a state) is moved to one abstract class called BaseAction and then every other action in the application extends it.
Why this design may be bad? First of all it has a big chance to become a „general bag” for any functionality which is needed in just limited part of the system. So you may have Activity1 and Activity2 which – let’s say – are similar in 35%. So this 35% of behavior goes to BaseActivity which becomes a parent class for both mentioned Activities.
But then in the future you will add Activity3,Activity4,Activity5 which will be part of a different process and won’t have this 35% of common logic – still every activity will inherit everything what some limited parts of your system may have in common. For example Activity3 will inherit methods,functions,fields which has only sense to Activity1.
Another problem with this approach may be something we can call „leaking template method.” It occurs when you have some abstract process which not applies to all activities like this one :
But then you can have an Activity which doesn’t belong to common process.
We can now of course split our BaseActivity to two separate base classes BaseActivityWithBusinessProcessSupport and BaseActivityWithoutHelp and even create GrandBaseActivity which would be a root for every activity but you see where it is going. This problem may still occur on every level of such hierarchy and number of abstract classes with specific abstract logic for specific parts of code may start rising dramatically.
It would be good to somehow add parts of functionality only when they are needed. We can not use abstract classes to solve this because you can extend only one. But we can inherit multiple interfaces. Can we somehow use them to mix functionalities? For inspiration let have a quick look on how other languages tried to solve this problem.
Can „BaseActivity” be (somehow) composed?
There is a language which seems to be implementing every possible feature which can be implemented on JVM – say hello to Scala.
Let’s have this simplified Activity with two „framework methods” and a trait which provides symbolic logging functionality:
Now what is a trait? It is something like interface with default method implementation and -in Scala – sometimes with fields. There is one more feature we can observe on the following example.
Look below at DefaultLifecycleLogging. It has this strange line : self:Activity with SomeLogger. This mean that it can be only inherited/mixed to a class which full fill two requirements
- The class can be seen as an Activity
- It has mixed SomeLogger trait
And because of those two demands we can assume in our trait that „it is an Activity and has access to methods from SomeLogger” . So in overriden method onStart from activity we can easily use debug method from SomeLogger.
When we run our sample code we should see that onStart was called directly from BusinessActivity and default version of onDestroy was provided by composed trait.
Why show examples in Scala when we are interested in Kotlin solution? Very often programmers mistake language limitation with design limitation. Both Scala and Kotlin are compiled to java bytecode so technically both should be able to produce similar executable code. On the language level Kotlin of course doesn’t have scala traits but even if we don’t have a tool then maybe still we can be able to simulate its mechanics.
But before that one more illustration of mixins in a language which is rapidly gaining popularity in mobile development world.
Another Illustration – Composing „BaseWidget” in Flutter
Flutter is a relatively fresh alternative for writing applications for Android (& iOS) . Primary building block in flutter is a Widget – which is something totally different from Activity but it doesn’t matter for this example.
There still may occur situation that we want to have a something like BaseWidget with some common functionalities… and we have the same problems as mentioned earlier when logic used „sometimes” is inherited by every building block even if it is not necessary.
So for example we want to have small cosmetic utility function which simplifies creation of onClick functions updating widget state. If I would write such anonymous function in a standard way it may look like this :
The level of indention raises dramatically. It would be good idea to declare this function externally and here just use the function reference but this problem will then occur for every onClick funcion.So the solution is to create a dedicated mixin which can enrich base widget class. Flutter uses Dart as a programming language and Dart has native support for mixins.
Also „self awareness” of mixins is supported by the language so we can do the same thing we did before with scala traits – state explicitly that it is mixed to a class which is or inherits from Widget State.
In the example mixin is like trait in scala and „on State” says that it can be only mixed … well on State.
So now take a look how onClick in our widget looks like when enriched with mentioned mixin.
Syntax is cleaner and mixin provide us nice wrapper which uses State functionality because it can – it has explicit declaration that it will be mixed on State .Ok we saw scala soultion, we saw Flutter/Dart solution. Now let see what is possible in Kotlin.
What is Possible In Kotlin
In our first attempt of simulating mixins in Kotlin we are going to use interfaces. Good news is that interfaces in kotlin can have default method implementations yet there are two serious limitations in comparison to scala or dart :
- No static type limitations on interfaces. So interface can not be aware of type families it will be attached.
- No private state which limits this approach to just adding behaviour implemented in methods.
And two „fake mixins” . First one provide functionality of creating text for logging input intent basic info.
and the second one – which is completely independent from the first one – just provide basic logging functionality constructed around some property taken from activity.
Finally we can assembly both interfaces in one Activity where we first use functionality of the first one to gather intent info and then functionality inherited from the second interface to log that info.
I agree that this mechanism is very limited and can only be used to combine pure behaviour but there is something more…
Adding State with Delegation
We can go further with this „mixin siumulation” but we are entering an area of some serious creative over engineering. Kotlin has an interesting feature where you can implement an interface and immediately provide delegate which provides functionality for this interface.So for example I want to have mixin with buffered logging functionality and I really need a a state somewhere to buffer messages. For start let’s just create a standard interface-implementation pair:
And thanks to delegates mechanism we can easily compose both with our activity
And this generally would work but we also needs to provide self implementation which is base for each „self aware mixin”. Our delegate is not an activity so it can not act as self directly. We need somehow to pass an activity which will extend it but this complicate situation because it creates a dependency cycle between activity and a delegate.The only way I was able to solve it was to bind activity to delegate after activity creation.So there is no complication time guarantee that binding would actually happen.
and small example of how this could work in Activity
Ok so what is happening here
- We need to have private constructor where we will have control over a creation of our delegate to be sure that binding is complete
- This is nice – Kotlin allows you to use specific object as a delegate – our object from private constructor
- And this will be public parameter less constructor which takes care of delegate bindings
As I said the line of over engineering is blurred here but it should provide help when we have to much functionality in BaseActivity. And of course you can „mix mixins” with BaseActivity approach. Both are just tools for your needs.I’ve checked decompiled Java in Intellij and generated bytecode should not have any nasty surprises.
In this article we took an inspiration from Scala and Flutter/Dart to create Kotlin solution which in my opinion may be helpful in specific situation. So first of all remember that you don’t need to create MegaUberBaseActivity with all possible common functionality because Kotlin has some nice feature to replace inheritance with composition.And also from time to time take a look at other technologies because solution for your problem may be already there. 🙂