Kotlin's Sealed class forms a part of an exciting feature: promoting flexibility and bringing functional programming concepts to an object-oriented language. It offers a restricted class hierarchy whereby a value can have one type from a limited set, bringing in type safety.
As we delve into this Kotlin Sealed class overview, keep in mind that a sealed class is an abstract class that uniquely restricts class hierarchies. When we say 'sealed', it creates a class with specific predetermined subclasses.
A Kotlin sealed class falls under the same compilation unit, and all its subclasses are nested within it. This contrasts with other classes where subclasses can extend a parent class from multiple places in your code. However, with Kotlin's sealed class, we restrict the class hierarchy to a defined set of types. It's the special keyword called 'sealed modifier' that makes this possible.
A sealed class has an important role to play in the extensive class hierarchies of Kotlin. It uses a 'sealed' keyword, denoting that the class is sealed, and the range of subclasses defined within it is final. It does not allow subclassing outside its containing file. This is why we say that a sealed class is implicitly abstract by itself and cannot be instantiated directly.
A sealed class offers two subclasses at least - using data classes or regular classes. Here’s an example:
1sealed class Shape { 2 data class Circle(var radius: Float) : Shape() 3 class Square(var length : Int, var breadth : Int) : Shape() 4}
In the above example, Shape is a sealed class with two subclasses - Circle and Square. The data class Circle and class Square are the two subclasses of the sealed class Shape.
Subclasses of a sealed class - for example, Sealed Class Shape - can be an object, a regular class, or even a data class. Also, sealed classes are not limited to a single instance - they can have multiple instances with different states.
Float times a sealed class in Kotlin offers a mix of benefits. It gives the type-safe feature that enum classes have, and also the flexibility provided by abstract classes. It restricts class hierarchies – but only to a certain level, allowing you to extend sealed classes within the same package. It provides a way to use functional programming in the otherwise objected-oriented Kotlin environment.
Considering the specific cases for using Kotlin sealed classes, we find this scope of application even in when expressions for different state cases. Using Sealed classes in when expressions eliminates the need for an else clause. Such form of expression is exhaustible, which means it covers all possible subclasses.
An insight into the enhanced class functionalities will follow in the upcoming sections with examples.
1sealed interface Result 2data class Success(val data: Any) : Result 3data class Error(val error: Exception) : Result
The above code is a perfect example of how sealed interfaces provide us with a type-safe way of handling various outcomes.
So, why do we use the Sealed class in Kotlin? First, Kotlin sealed classes bring functional programming concepts to the table, enhancing our ability to model complex systems. They support advanced form modeling and enforce a finite class hierarchy, ensuring code safety and eliminating runtime errors.
With abstract classes and sealed classes, Kotlin provides us with a way to represent restricted class hierarchies. Abstract classes allow open hierarchies, while sealed classes offer more restrictions, thus reducing runtime errors. This forms the backbone of why we use sealed classes in Kotlin.
Secondly, Kotlin sealed classes make our when expressions exhaustive. This means, you won't need to use an else branch in when expressions, unlike with other classes. This exhaustive nature of when expressions helps you handle all known subclasses of a sealed class at compile time, which enhances code safety.
In simple terms, when you know exactly what you're dealing with – sealed classes are the way to go!
1sealed class SealedClassShape { 2 data class Circle(var radius: Float) : SealedClassShape() 3 data class Rectangle(var length: Float, var breadth: Float): SealedClassShape() 4 data class Square(var side: Float) : SealedClassShape() 5} 6 7fun main() { 8 val circle: Circle = Circle(3.5F) 9 val rectangle: Rectangle = Rectangle(6.5F, 3.2F) 10 val square: Square = Square(4.0F) 11 println(circle.radius) 12 println(rectangle.length) 13 println(square.side) 14}
In the above example, the when expression defines all the possible subclasses of the SealedClassShape. Hence, there is no need for an else branch. This is one of the biggest advantages of sealed classes in Kotlin. It provides complete type safety in conditional expressions at compile time.
Knowing when sealed class Kotlin would be optimal can help encapsulate your code with more flexibility and maintainability.
Use sealed classes when:
• An object can have one type out of a small set of possible types.
• You want to verify every type of class without the need for an else clause in a when expression.
• You want to leverage the benefits of functional programming's exhaustive type checking.
• You need a restricted class hierarchy with a strictly limited number of subclasses.
• You want to represent a fixed set of operations, or dispatch actions, particularly in state management.
Let's take a look at a quick snippet:
1sealed class Result{ 2 data class Success(val data: Any) : Result() 3 data class Error(val message: String) : Result() 4 object Loading : Result() 5} 6 7fun display(result: Result){ 8 when(result){ 9 is Result.Success -> println(result.data) 10 is Result.Error -> println(result.message) 11 is Result.Loading -> println("Loading...") 12 } 13}
In the above code, when expression is used for different state cases. Since Result is a sealed class, all subclasses are known at compile time. Hence, when checks through each possible subclass, and there is no need for an else clause.
Kotlin's when expression is a much more powerful version of the traditional switch statement found in other languages, especially when used in combination with sealed classes. It's capable of far more than just checking a variable against a series of values.
When Sealed Class Kotlin is used in when expressions, it offers exhaustiveness, meaning all branches of execution are accounted for at compile time itself. This enhanced when expression ensures that all subclasses of a sealed class are covered, providing safety against missed scenarios.
Here's an example:
1sealed class Shape { 2 class Circle(var radius: Float) : Shape() 3 class Square(var side: Float) : Shape() 4} 5 6fun eval(shape: Shape) = 7 when(shape) { 8 is Shape.Circle -> "The shape is a circle with radius ${shape.radius}" 9 is Shape.Square -> "The shape is a square with side ${shape.side}" 10 } 11 12fun main() { 13 val circle: Shape.Circle = Shape.Circle(3.2F) 14 println(eval(circle)) 15 16 val square: Shape.Square = Shape.Square(4.5F) 17 println(eval(square)) 18}
In this snippet, Shape is a sealed class with two subclasses - Circle and Square. The fun eval function uses a when expression to find out the type of shape. When this code compiles, Kotlin ensures that all possible subclasses are handled in the when expression, allowing you to forego the usual else branch.
Traditionally in object-oriented programming, inheritance is used to create a class hierarchy. However, Kotlin offers a unique way to represent restricted class hierarchies by using sealed classes.
A simple sealed class hierarchy looks something like this:
1sealed class SealedClassA { 2 class B : SealedClassA() 3 class C : SealedClassA() 4 class D : SealedClassA() 5}
In this simple example, SealedClassA is the root class, and B, C, and D are subclasses. A key fact about sealed class hierarchies is that all the subclasses must be defined in the same file as the sealed class.
You can't add new subclasses to a sealed class hierarchy in a separate compilation unit. This means that the Kotlin compiler knows about all possible subclasses at compile-time, which can greatly enhance type safety.
In the hierarchy above, we are certain that SealedClassA is either B, C, or D - and could be nowhere else. In other words, the class hierarchy is restricted to known types only, defined within the same file.
Members and functions can be defined within the above subclasses B, C, and D in the same way as you would for any other class. This allows us to have a controlled and predictable class hierarchy, providing powerful modeling capabilities in representing complex domains.
It's natural to wonder whether you can use interfaces with sealed classes, or mix them with other types of classes. Here’s a quick look:
1sealed class SealedClassExample { 2 data class Success(val data: String) : SealedClassExample() 3 4 sealed class Error : SealedClassExample() { 5 data class NetworkError(val message: String) : Error() 6 data class UnknownError(val unknownErrorMessage: String) : Error() 7 } 8}
Here, SealedClassExample is a sealed class with two subclasses Success and Error. Error is a sealed class by itself containing two data classes NetworkError and UnknownError. This example demonstrates how sealed classes can be nested within other sealed classes.
A sealed class can also implement interfaces as required:
1sealed class Shapes : Drawable { 2 data class Circle(val radius: Float) : Shapes() 3 data class Rectangle(val length: Float, val breadth: Float) : Shapes() 4 data class Square(val side: Float) : Shapes() 5}
In this code, Drawable is an interface that the sealed class Shapes implements. Any object of Shapes becomes Drawable, which showcases the fact that sealed classes can also implement interfaces.
In conclusion, Kotlin's sealed classes allow you a great level of flexibility and safety when modeling your classes and hierarchies.
During this informative Kotlin Sealed class journey, we've explored its definition, benefits, usage, and much more. Let's summarize the key points:
Sealed classes provide a way to represent restricted class hierarchies, allowing you to gain type safety and flexibility in Kotlin.
Sealed classes have two subclasses typically – data classes and regular classes.
They offer exhaustive type checks at compile time when used with when expressions. This eliminates the requirement for an else clause, enhancing code safety.
Sealed classes are an effective way to model state in applications, especially in functional programming constructs.
Despite being abstract, sealed classes can still host common code that can be shared among subclasses.
While the sealed class itself cannot be a data class, its subclasses can be.
Sealed classes are powerful modeling tools for complex domains but should be used judiciously for concise code.
This tutorial endeavored to elucidate the concept of a Kotlin Sealed class. It's hoped that the insights shared here unraveled the mystery surrounding sealed class and explain why it is worth considering when designing your class hierarchy in Kotlin.
It is hoped that the knowledge collected will help brighten your next programming project, whether it be a complex system needing a sealed class hierarchy or a simple application where knowing at compile time what the possibilities may be is a boon.
May your journey into Kotlin's sealed class be sealed with success! Happy Kotlin coding!
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.