A lot of times developers fail to address all possible conditions in the code which leads to unexpected behavior.

Let’s say, we want to build an application for an automobile industry where the user can select the vehicle. We have two options for the vehicles Car and Truck. The following code snippet is a representation of our application.

abstract class Vehicle()
class Car(val name: String, val modelNumber: String, val color: String) : Vehicle()
class Truck(val name: String, val modelNumber: String, val color: String) : Vehicle()

fun chooseVehicle(vehicle: Vehicle): String {
    return when(vehicle) {
        is Car -> {
            println("Its a car")
            vehicle.toString()
        }
        is Truck -> {
            println("Its a Truck")
            vehicle.toString()
        }
        else -> ""
    }
}

Note that even though we have just two options for vehicles and both of them are mentioned in the when statement we must have else statements as well otherwise we will get “when statement must be exhaustive, add necessary else branch ”.  

If we decide to add one more vehicle called Bus, then the code will look as follows.

abstract class Vehicle()
class Car(val name: String, val modelNumber: String, val color: String) : Vehicle()
class Truck(val name: String, val modelNumber: String, val color: String) : Vehicle()
class Bus(val name: String, val modelNumber: String, val color: String) : Vehicle()

fun chooseVehicle(vehicle: Vehicle): String {
    return when(vehicle) {
        is Car -> {
            println("Its a car")
            vehicle.toString()
        }
        is Truck -> {
            println("Its a Truck")
            vehicle.toString()
        }
        else -> ""
    }
}

fun main(args: Array<String>) {
    chooseVehicle(Bus("Volvo", "V1", "blue"))
}

Since Bus is passed as a vehicle, the else statement will be executed instead of the code corresponding to the Bus which is not present there. Because of the else statement, code compiles and it makes our debugging very difficult. If we want to avoid writing else conditions in our code and instead handle all possible combinations then we can use sealed classes.

First let’s learn how the sealed class looks and its rules before we jump into how we can use it to solve the above problem.

sealed class Vehicle
class Car(val name: String, val modelNumber: String, val color: String) : Vehicle()
class Truck(val name: String, val modelNumber: String, val color: String) : Vehicle()
class Bus(val name: String, val modelNumber: String, val color: String) : Vehicle()

Rules of Sealed class

  • All the subclasses of the sealed class should be in the same file.
  • It has a private constructor.
  • It is an abstract class by default so we do not have to specify a keyword open even though it has subclasses.
  • It cannot be instantiated.

Use of Sealed class

When we use a sealed class with when statement we must provide the all possible type of subclasses of the Sealed class as an option and the else statement is not necessary.

sealed class Vehicle
class Car(val name: String, val modelNumber: String, val color: String) : Vehicle()
class Truck(val name: String, val modelNumber: String, val color: String) : Vehicle()

fun chooseVehicle(vehicle: Vehicle): String {
    return when(vehicle) {
        is Car -> {
            println("Its a car")
            vehicle.toString()
        }
        is Truck -> {
            println("Its a Truck")
            vehicle.toString()
        }
    }
}

If we add another vehicle Bus then the chooseVehicle method shows an error as notifying us that some conditions are not handled.

sealed class Vehicle
class Car(val name: String, val modelNumber: String, val color: String) : Vehicle()
class Truck(val name: String, val modelNumber: String, val color: String) : Vehicle()
class Bus(val name: String, val modelNumber: String, val color: String) : Vehicle()

fun chooseVehicle(vehicle: Vehicle): String {
    return when(vehicle) { // throws error because the condition for Bus is not added 
        is Car -> {
            println("Its a car")
            vehicle.toString()
        }
        is Truck -> {
            println("Its a Truck")
            vehicle.toString()
        }
    }
}

Utilizing a sealed class will ensure that we address all possible cases and it is a very important feature to reduce the possible error at run time.

sealed class Vehicle
class Car(val name: String, val modelNumber: String, val color: String) : Vehicle()
class Truck(val name: String, val modelNumber: String, val color: String) : Vehicle()
class Bus(val name: String, val modelNumber: String, val color: String) : Vehicle()

fun chooseVehicle(vehicle: Vehicle): String {
    return when(vehicle) { 
        is Car -> {
            println("Its a car")
            vehicle.toString()
        }
        is Truck -> {
            println("Its a Truck")
            vehicle.toString()
        }
        is Bus -> {
            println("Its a Bus")
            vehicle.toString()
        }
    }
}

Go to the next article to continue reading.

Category
Tags

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *