When to Switch: Kotlin when statement vs Java switch

When to Switch: Kotlin when statement vs Java switch

The switch statement (or something akin to it) is a staple of most programming languages and is something every programmer from novice to professional will already be familiar with. However, for the avoidance of doubt the purpose of a switch statement is to execute a branch of logic depending on the state of a variable, thus changing the control flow of the program’s execution. One of the common use cases for reaching for the switch statement is when we have several, nested if-else constructs. Refactoring to use a switch instead improves the readability of the code.

                        
                            enum Restaurants { MCDONALDS, FIVEGUYS, BURGERKING, KFC, POPEYES, PIZZAHUT, WAGAMAMA, NANDOS, SPUDULIKE }

Restaurants whereIWantToGo = Restaurants.FIVEGUYS;
String Location = "";

switch (whereIWantToGo) {
	case MCDONALDS:
	case PIZZAHUT:
	case NANDOS:
		location = "Westfield";
		break;
	case WAGAMAMA:
		location = "Covent Garden";
		break;
	case KFC:
		location = "Bishopsqate";
		break;
	default:
		location = "deliveroo";
}
                        
                    

Some will already be screaming at the screen that switch statements are code smells and should be avoided, and in most cases, you’d be correct, but please bear with me for the purposes of where this example leads. Ideally, in OO languages anyway, we’d leverage dependency injection and polymorphism to avoid using the switch statement. Specifically, we could create an interface and a bunch of implementations of that interface for each branch of the switch statement and refactor it away. The desired implementation will then be attained via polymorphism based on the concrete object class injected to the encapsulating class or method.

At the risk of digressing further, let’s get back to where we started, the switch statement. Java’s offering of this language construct has evolved over time. More types are now ‘switchable’, among them, bytes, shorts, integers, chars, enums and strings. With the release of JDK13 Oracle shook things up a fair bit with the switch statement, in terms of look and feel anyway. As well as the standard switch ‘statement’ offering there is now also the switch ‘expression’ construct. It reads a lot more like a Kotlin ‘when’ statement, has comma delimited criteria, no longer falls through cases (you don’t need to use break statements) and like the Kotlin ‘when’ requires that all possible cases are covered (when switching on an enum for example).

                        
                            enum Restaurants { MCDONALDS, FIVEGUYS, BURGERKING, KFC, POPEYES, PIZZAHUT, WAGAMAMA, NANDOS, SPUDULIKE }

Restaurants whereIWantToGo = Restaurants.FIVEGUYS;

var location : String = switch (whereIWantToGo) {
	case MCDONALDS, PIZZAHUT, NANDOS -> "Westfield";
	case WAGAMAMA -> "Covent Garden";
	case KFC -> "Bishopsgate";	
	default -> "deliveroo";
};
                        
                    

Evolved as it has the humble switch statement (or expression) in Java still has a lot less under the hood than its counterpart in Kotlin, namely the ‘when’ statement. The biggest limiting factor being the fact the most complex type we can switch on is the Enum type, which aside from its explicit representation offers no additional room to manoeuvre in terms of additional state or computation, mainly because they are singletons.

To understand the full power of Kotlin’s counterpart to the switch statement we must first try to understand sealed classes. In Kotlin sealed classes have a strict inheritance hierarchy with an explicit set of subclasses which must be known at compile time. They are implicitly abstract and allow us to define a base representation of the class then multiple derivations of this base class with varying fields and functions therein. These principles extend further to the notion of sealed interfaces which embody the same characteristics but allow us to circumvent the restriction of multiple inheritance that exists with sealed classes. The use case for sealed classes is typically when we have multiple, known flows of control in our unit of code, the same scenario in which we could elect for a switch construct in another language.

                        
                            sealed class Restaurant (val foodType: String, val isVeganFriendly: Boolean = false)
object MCDONALDS: Restaurant(foodType: "burgers")
object FIVEGUYS: Restaurant(foodType: "burgers")
object BURGERKING: Restaurant(foodType: "burgers")
object KFC: Restaurant(foodType "fried chicken")
object POPEYES: Restaurant(foodType: "fried chicken")
object NANDOS: Restaurant(foodType: "chicken")
object PIZZAHUT: Restaurant(foodType: "pizza")
object WAGAMAMA: Restaurant(foodType: " noodles", isVeganFriendly: true)
object SPUDULAIKE: Restaurant(foodType: "baked potatoes", isVeganFriendly: true)
                        
                    

As alluded to previously what sets Kotlin’s sealed classes apart from Java’s offering is when we use them in conjunction with the ‘when’ construct. This feature in Kotlin is akin to a Java switch expression on steroids. While in Java we are restricted to using primitives, their boxed types, strings and enums with switch statements or expressions in Kotlin we can use the when statement with any built in or user-defined type. Furthermore, unlike Java the cases of the when statement don’t need to be constant expressions and can in fact be any dynamic expression, evaluated at runtime so long as the return type of that expression matches the type(s) evaluated in the when block.

                        
                            fun whereIWantToGo(): Restaurant = FIVEGUYS

var location = when(whereIWantToGo()) {
	is MCDONALDS, PIZZAHUT, NANDOS -> "Westfield"
	is WAGAMAMA -> "Covent Garden"
	is KFC -> "Bishopsqate"
	else -> "deliveroo"
}
                        
                    

There are many positive upshots to this. We can use when statements far more liberally in our Kotlin code than we could with switch statements in Java because we can use them with any object we desire. The when construct also reads far more like plain English than even an if statement and as such we can elect to use the former in place of the latter. This can make our code more readable and consistent when we have more than a single flow of control across our various units of code. We can use sealed classes in conjunction with when statements to better articulate the exhaustive sets of strongly typed outcomes for a particular piece of logic. If we add a new child class to such an outcome class we will get a compile time error if we haven’t catered for this outcome in any respective when statements or expressions.