Topic: 4 Exploring the types of classes in Android

This is our third topic from learn android from basic to advance series

Β·

10 min read

Topic: 4 Exploring the types of classes in Android

Hello Devs, This is our fourth blog from the Learn Android from Basic to Advanced series. As you know we talk about classes in our first topic Understanding OOPs Concepts. So, did you know how many types of classes are available on Android? If you already know then it's good but if you not then don't worry about it we catch up on this together. Let's start the blog.

Types of classes

I am giving you a quick intro about what is class this is only for our new devs who join an Android journey. So basically class is a blueprint of our application. In Android, we create classes to define the structure and behavior of various components such as activity, fragment, and custom view classes. OK moving on to the classes type.

  • Data class

  • Enum class

  • Sealed class

  • Abstract class

  • Singleton class

  • Nested class

  • Inner Nested class

Alright devs, Let's explore all these classes one by one and understand why and where these classes are used during Application Development.

Data class

Data class is a special type of class that helps us to hold data. The data class must have one item in the parameter. It's concise and automatically generates several standard methods such as equals(), hashCode(), toString(), componentN() functions (for destructuring declarations), and copy() function. This class was very helpful when you fetch data from the server or you want to send data as a request body in API. We call this class as Model class or DTO (Data Transfer Object).

Data classes are defined using the data keyword followed by the class definition. Inside the class, you define properties that represent the data you want to hold. These properties are automatically included in the generated methods.

By default, data class properties are immutable (read-only) unless explicitly defined as var. This immutability ensures that once an object is created, its properties cannot be changed, enhancing predictability and reducing the risk of unexpected behavior.

Data classes automatically generate equals() and hashCode() methods based on the properties declared within the class. This makes it easy to compare objects for equality and use them in data structures like HashSet or HashMap.

The toString() method generated for data classes provides a human-readable string representation of the object, which is helpful for debugging and logging.

The copy() function allows you to create a copy of an existing data class object with modified properties. This is useful when you want to update certain properties while keeping the rest unchanged.

data class User(
 val id: String,
 val name: String,
 val number: Int,
 val isActive: boolen
)

This is the way you structure your data class and add the necessary parameters in your data class. Now move on to the next class from our types of classes the Enum class.

Enum class

Enum class is a special type of class that represents a group of constants. Each constant in the enum class is the object. This class was very interesting i mean imagine you have several choices in your food type and according to this choice, you categorize your food. But how can you filter this obviously by the food type right so this class is helping us to create this type.

Enum classes are commonly used to define a fixed set of related constants. These constants represent different states, types, or options within your application.

Enum classes provide type safety, which means that you can only assign one of the predefined constants to a variable of that enum type. This helps prevent runtime errors caused by using incorrect or invalid values.

Enum classes improve code readability and maintainability by providing descriptive names for each constant. This makes it easier for developers to understand the purpose and usage of the constants.

Enum classes are often used with when expressions (Kotlin's replacement for Java's switch statement) for concise and readable branching based on the enum's value.

  1. Create enum class MyFoodType.
enum class MyFoodType {
  FAST_FOOD,
  VEG_FOOD,
  NON_VEG_FOOD,
  SEA_FOOD,
  CHINES_FOOD
}
  1. Use this class
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val foodType = MyFoodType.FAST_FOOD

        val foodDescription = when (foodType) {
            MyFoodType.FAST_FOOD -> "This is fast food."
            MyFoodType.VEG_FOOD -> "This is vegetarian food."
            MyFoodType.NON_VEG_FOOD -> "This is non-vegetarian food."
            MyFoodType.SEA_FOOD -> "This is seafood."
            MyFoodType.CHINESE_FOOD -> "This is Chinese food."
        }

        println(foodDescription)
    }
}

This is the way you can create and use enum class on your Android development. Enum constants can also have associated data. This means you can define properties or methods for each constant, allowing for more complex behavior. This is particularly useful when each constant requires specific functionality. Now moving on to the next class from the types of classes list.

Sealed class

A sealed class is a class that restricts the types of its subclasses. It allows you to represent restricted hierarchies in a more concise way compared to using traditional inheritance.

A sealed class can only be extended within the file where it's declared. This means all subclasses must be defined within the same file as the sealed class itself. This restriction ensures that all possible subclasses of the sealed class are known at compile time.

Sealed classes are beneficial in representing restricted hierarchies, such as when dealing with different states or types in your application. For example, you might use a sealed class to represent the different states of a network request (e.g., Loading, Success, Error), or the different types of data your UI can display (e.g., Image, Text, Video).

Sealed classes are often used in conjunction with Kotlin's when expression for pattern matching. This allows you to handle different cases of the sealed class in a concise and type-safe manner.

Sealed classes can have companion objects just like regular classes. This allows you to define factory methods or other utility functions related to the sealed class.

sealed class Result<out T> {
    data class Success<out T>(val data: T) : Result<T>()
    data class Error(val message: String) : Result<Nothing>()
}

In this example, Result is a sealed class representing the result of an operation. It has two subclasses: Success and Error. Success holds a result of type T, while Error holds an error message. The use of a sealed class ensures that all possible outcomes of the operation are explicitly defined.

When using sealed classes with when expressions, the compiler can enforce exhaustiveness checks. This means you must handle all possible subclasses of the sealed class, which helps prevent bugs caused by missing cases. Now moving on to the next type from our types of classes list.

Abstract class

An abstract class is a class that cannot be instantiated directly and may contain abstract methods or properties. Abstract classes are designed to serve as blueprints for other classes, providing common functionality that subclasses can inherit and customize.

Abstract classes can declare abstract methods or properties using the abstract keyword. These methods or properties are defined without implementation and must be overridden by concrete subclasses.

Abstract classes can contain both abstract and non-abstract methods or properties. Abstract methods provide a way to enforce specific behavior in subclasses, while non-abstract methods can provide default implementations that subclasses can choose to override.

Subclasses of an abstract class must either implement all abstract methods and properties or be declared as abstract themselves. This ensures that concrete subclasses provide complete implementations of the abstract class's contract.

Abstract classes are commonly used to define common behavior or structure shared among multiple related classes. In Android development, abstract classes are often used to define base classes for activities, fragments, adapters, or other components, providing common functionality that can be reused across different parts of the application.

abstract class Shape {
    abstract fun area(): Double
    abstract fun perimeter(): Double
}

class Circle(val radius: Double) : Shape() {
    override fun area(): Double {
        return Math.PI * radius * radius
    }

    override fun perimeter(): Double {
        return 2 * Math.PI * radius
    }
}

class Rectangle(val width: Double, val height: Double) : Shape() {
    override fun area(): Double {
        return width * height
    }

    override fun perimeter(): Double {
        return 2 * (width + height)
    }
}

In this example, Shape is an abstract class with abstract methods area() and perimeter(). Circle and Rectangle are concrete subclasses of Shape that provide implementations for these abstract methods.

Abstract classes facilitate polymorphism, allowing subclasses to be treated as instances of the abstract class. This enables more flexible and modular code design, as subclasses can be used interchangeably where the abstract class is expected.

Abstract classes can be extended by other abstract classes, enabling the creation of hierarchical class structures with multiple levels of abstraction. This allows for the refinement and extension of behavior in a modular and organized manner. Now moving on to the next class Singleton class.

Singleton class

A Singleton class is a class that restricts the instantiation of a class to a single instance and provides a global point of access to that instance.

Singleton classes are commonly used to manage resources that should have a single, shared instance across the application. Examples include database handlers, network managers, preference managers, and logging utilities.

Singleton instances are typically lazily initialized, meaning they are created when they are first accessed rather than when the application starts. This optimizes memory usage by deferring object creation until it's actually needed.

Singleton classes must ensure thread safety, especially in multi-threaded environments like Android applications. This can be achieved using various synchronization techniques such as double-checked locking, synchronized blocks, or by using Kotlin's lazy delegate.

There are different ways to implement Singleton classes in Kotlin. One common approach is to use a companion object to hold the instance and provide a method to access it.

class MySingleton private constructor() {
    companion object {
        @Volatile
        private var instance: MySingleton? = null

        fun getInstance(): MySingleton {
            return instance ?: synchronized(this) {
                instance ?: MySingleton().also { instance = it }
            }
        }
    }
}

In this example, MySingleton is a Kotlin class with a private constructor to prevent direct instantiation. The getInstance() method returns the singleton instance, creating it if necessary using double-checked locking for thread safety.

When using Singleton classes in Android, it's essential to consider the application's lifecycle. Singleton instances should be initialized and released appropriately to avoid memory leaks and resource waste. Using application or activity lifecycle callbacks (e.g., onCreate(), onDestroy()) can help manage the lifecycle of Singleton instances. Now moving on to the next class from our list is the Nested Class.

Nested class

A nested class is a class that is defined within another class. It's important to note that nested classes in Kotlin are static by default, meaning they don't hold a reference to an instance of the outer class.

Nested classes can have different visibility modifiers like private, protected, internal, or public, just like regular classes. The default visibility modifier for nested classes is private.

Nested classes have access to members (both properties and functions) of the outer class. However, the reverse is not true – the outer class doesn't have access to members of the nested class unless they are explicitly exposed.

Kotlin supports both instance and static nested classes. An instance nested class requires an instance of the outer class to be instantiated, whereas a static nested class does not.

Nested classes can be useful for grouping related functionality within a class, encapsulating helper classes, or improving code organization. They can also be helpful for implementing callbacks or listeners where access to the outer class context is required.

class OuterClass {
    private val outerProperty: Int = 10

    class NestedClass {
        fun nestedMethod() {
            // Can't access outerProperty here
        }
    }
}

In this example, NestedClass is a nested class within OuterClass. It doesn't have access to the outerProperty unless it's passed as a parameter or exposed through a method. Now moving on to the last class from the type of classes list.

Inner Nested class

Inner classes, which are non-static nested classes. Inner classes hold a reference to an instance of the outer class and can access its members directly. They are defined using the inner keyword.

class OuterClass {
    private val outerProperty: Int = 10

    inner class InnerClass {
        fun innerMethod() {
            println(outerProperty) // Can access outerProperty directly
        }
    }
}

In this example, InnerClass is an inner class within OuterClass. It can access the outerProperty directly because it holds a reference to an instance of the outer class.

When working with inner classes, be cautious of potential memory leaks. Inner classes hold references to the outer class, which can prevent the outer class instance from being garbage collected if not handled properly.

We cover all of the classes from the types of classes list. It's time to wrap up our blog and I hope this blog helps you to understand all the classes very well. So we meet on our next blog from this series Android Variables.


Connect with Me:

Hey there! If you enjoyed reading this blog and found it informative, why not connect with me on LinkedIn? 😊 You can also follow my Instagram page for more mobile development-related content. πŸ“²πŸ‘¨β€πŸ’» Let’s stay connected, share knowledge and have some fun in the exciting world of app development! 🌟

Check out my Instagram page

Check out my LinkedIn

Β