Sealed classes made easy

https://preview.redd.it/g462jh94wkgc1.jpg?width=1080&format=pjpg&auto=webp&s=a760dbe640814cae93c4153de30bb7701af46adf

What are sealed classes

Sealed classes in Kotlin are like exclusive clubs for your class hierarchies. They allow you to define a set of ‘members’ or subclasses that are welcome to join, but they firmly close the door to any unexpected guests. With sealed classes, you can create structured and predictable class hierarchies, making your code more organized and maintainable.

To declare a sealed class, you simply use the ‘sealed’ keyword. This enforces a structured approach to your class hierarchy and ensures that only predefined subclasses can exist.

https://preview.redd.it/8f7etjq6zkgc1.jpg?width=1736&format=pjpg&auto=webp&s=43b8d5148323156ce4d73717feba83ad0da3a8f5

Practical examples

Imagine you’re building a program that deals with different shapes: circles, squares, and triangles. Sealed classes can help us create a well-organized hierarchy for these shapes.

https://preview.redd.it/826blc7azkgc1.jpg?width=1736&format=pjpg&auto=webp&s=744fa631de4d8e8ec64c4af6eea4a1cbd3bf9345

In our code, we define a sealed class called Shape and create three subclasses: Circle, Square, and Triangle. This restricts our hierarchy to only these three shapes — no more, no less.

https://preview.redd.it/tsoyzpnczkgc1.jpg?width=1736&format=pjpg&auto=webp&s=6755369730e6cfaa90e03e29c390d6ac1e75b3a2

Then, we use sealed classes in pattern matching with a calculateArea function. This allows us to handle each shape type differently based on the subclass, providing type safety and clarity in our code.

Use cases

Sealed classes aren’t just for shapes; they have practical use cases in the real world.

https://preview.redd.it/ivt4firgzkgc1.jpg?width=1736&format=pjpg&auto=webp&s=9536a93058a4ea8915f162e6b5cdc2dfff1eb9c5

For example, you can use them to represent different states of API responses, such as success, error, or loading.

https://preview.redd.it/u79ge59jzkgc1.jpg?width=1736&format=pjpg&auto=webp&s=190908c8b049fb64088997288ff2755c6508b4b8

We create sealed classes for ApiResponse and its subclasses like Success, Error. This makes handling various scenarios when communicating with a server much easier and more structured.

https://preview.redd.it/ntdejcplzkgc1.jpg?width=1736&format=pjpg&auto=webp&s=a3cc2200f9bd1652892d4d7fa610b878250bb0bd

Now that we’ve covered the basics, let’s explore a more advanced use case for sealed classes. Imagine you’re working on a complex application that involves managing states — a common scenario in game development, user interfaces, or workflow management.

In such cases, a Finite State Machine (FSM) can be a powerful tool. It allows you to model different states and transitions between them, ensuring that your application behaves consistently.

Let’s create a simplified FSM using sealed classes. We’ll define a sealed class State that represents various states our application can be in, such as Idle, Loading, Running, and Error. Each state can have its own associated data and behavior.

https://preview.redd.it/jlyns0pnzkgc1.jpg?width=1400&format=pjpg&auto=webp&s=a164c7f29fd20598780697afec834730f138ef56

  • Idle: Represents the initial state when the application is not doing any work.
  • Loading: Indicates that the application is currently loading data or performing an operation.
  • Running: This state includes a progress value to represent the progress of a task or operation.
  • Error: Represents an error state with an associated error message.

https://preview.redd.it/2ojtto2qzkgc1.jpg?width=1400&format=pjpg&auto=webp&s=421511dc832f27980d31ea472d9dfa656ff58ebb

Next, in our ViewModel class we want to expose only one field that would dictate our UI state which we would listen in our screen. In this case initial value (since MutableStateFlow needs an initial value) is Idle since nothing is happening. Once we trigger our function we update our state to Loading, and then update it again to Success/Error depending on what we get back from our request.

MutableStateFlow exposes update method which is thread safe, and its clearly indicating that our flow (in this case our state) is being updated.

https://preview.redd.it/2jnqxa9vzkgc1.jpg?width=1400&format=pjpg&auto=webp&s=def24bd083f7b365f11f5f23bd2561868272a561

Finally, we (using by delegate) get the value of our state from the ViewModel (you can also use equals (=) sign, in which case to access the value from the state you’d use state.value). In this case, collectAsStateWithLifecycle() call is needed because composable objects are not lifecycle aware so we need make sure that our data is collected only when we want it. Our state is then passed into our private composable screen function where, depending on the value we get back and plugged into our exhaustive when statement, we render different parts of the screen.

Best practices

Structured Class Organization

Keep sealed classes, their subclasses, and related functions in separate files or well-structured packages. This promotes clarity and maintainability by making it easy to locate and manage sealed class hierarchies.

Exhaustive ‘when’ Expressions

Always ensure exhaustive ‘when’ expressions when working with sealed classes. This ensures that you handle all possible subclasses, reducing the risk of unintended behavior when new subclasses are introduced.

Consider Data Classes

If your sealed class subclasses primarily hold data, consider making them data classes. This simplifies code and promotes immutability, which is often desirable when modeling different states or types.

Comprehensive Documentation

Add meaningful comments and documentation to sealed classes and their subclasses. Explain the purpose and usage of each class to make it easier for other developers to understand your code and its intended usage.

Thorough Testing

Write comprehensive unit tests that cover all possible states or subclasses of your sealed classes. Testing ensures that your code behaves as expected in various scenarios and helps catch potential issues early in development.

Avoid Excessive Nesting

Avoid deeply nesting sealed classes within other classes or structures. Excessive nesting can lead to code that is hard to read and maintain. Keep your class hierarchies as flat as possible for clarity.

Conclusion

Sealed classes in Kotlin offer a powerful mechanism for creating well-structured and restricted class hierarchies. By providing a closed set of subclasses, sealed classes ensure clarity, type safety, and maintainability in your code. Whether you’re modeling different states, response types, or complex structures, sealed classes prove to be a versatile tool. Remember to follow best practices, keep your code organized, and explore advanced use cases to harness the full potential of sealed classes in your Kotlin projects.

submitted by /u/average-alchemist
[link] [comments]