Learning by Example

Kotlin: Sealed Classes

A concrete example of when you might use sealed classes in Android

Sealed Classes, not Seal Classes!

There have already been quite a few articles written on the topic of Kotlin sealed classes and so I am not going to rehash here what I believe others to have already adequately covered. Instead this article will demonstrate a practical example of utilizing sealed classes in Android code so that you can get a better idea of how and when you might leverage them.

In our example we will build off of the Creating an Android App Launcher article that I wrote last month. In that article I showed how one might go about creating a feature within their app for launching other, third-party apps. If you haven’t already checked that article out then you may want to jump over and give it a quick read. If you did already read the article and also checked out the accompanying GitHub sample project then you may have noticed that inside of the AppLauncherViewModel class there was an inner, sealed class named App and one concrete subclass of App named AppInfo. Here they are again for the sake of convenience:

With our sealed class App and its lone concrete implementation class AppInfo in place we are now able to do the following within our AppLauncherAdapter class when determining how to present and handle instances of App in our small app launcher dialog as demonstrated below:

Now this works well enough, but let’s go ahead and imagine that our product owner later comes to us saying Hey, <insert your name here>, it sure would be pretty cool if in addition to third party apps, we could also show some of those fancy new Android Q Settings Panels directly from our App Launcher dialog.

Interesting idea hypothetical product owner! We know that our current AppInfo class allows for us to specify a label and an icon, both of which we’ll probably also want for displaying our shortcuts for the Settings Panels, but unfortunately the click handler that is currently called for our AppInfo objects results in us launching the app using the launching intent of the underlying app package which is not at all what we need for showing the Settings Panels.

So how might we go about implementing this? Let’s extend our sealed class hierarchy shall we? Now, rather just one subclass of App we will instead have the following class hierarchy:

The updated code representing the class hierarchy depicted above is shown in detail in the following gist:

As you can see we have simply created a second subclass of App called SettingsPanel that accepts a label and icon, similar to AppInfo, but also a String representing the Settings Panel to display. This String should be one of the Settings.Panel constants provided by the Android SDK found here. Note also that the app icon field was moved up into the App class as it has proven to be a common attribute amongst both subclasses.

With this updated class hierarchy we can now update our adapter class to support both apps and settings panels similar to the following:

It is worth noting that if we knew ahead of time that the only Settings Panel that we would ever support is, for example, the Volume Settings Panel then we could have just made a SettingsPanelVolume object (singleton) that inherited from App rather than the more generic SettingsPanel data class. We went the data class route in this example so that we could support any of the Settings Panels that are supported both today and in the future.