1. Introduction

Util classes, short for utility classes, are classes that typically contain static methods and constants. Util classes play the crucial role of encapsulating core functionality for the application. These classes are not meant to be instantiated, and their primary purpose is to provide a set of reusable functionalities.

In this tutorial, we’ll learn what util classes are in Kotlin, explore their significance, provide code examples, write corresponding tests, and showcase practical use cases.

2. Why Are Util Classes Important?

Util classes play a crucial role in software development. First, they adhere to the single responsibility principle by concentrating on a specific set of utility functions. This ensures that each util class has a well-defined purpose, contributing to the overall clarity and maintainability of the codebase.

Additionally, util classes often feature static methods and constants, eliminating the need for instantiation, which promotes efficiency. By encapsulating utility functionalities in dedicated classes, we promote the separation of concerns within our project. This isolation helps maintain a clean and modular code structure, making it easier for us to manage and enhance the application logic.

3. Creating Util Classes

Next, we’ll learn how to craft util classes in Kotlin and test them.

In Kotlin, the object keyword guarantees that we only have one instance of our util class at runtime. This Kotlin feature ensures a singleton pattern, meaning that there’s a single, globally accessible instance of the object with any utility functions placed inside. We’ll use the object keyword instead of the class keyword when declaring our util classes.

Let’s start by creating a simple util class that calculates the square of a given number:

object MathUtils {
    fun square(number: Int): Int {
        return number * number
    }
}

Now, let’s test our MathUtils class:

@Test
fun `Should Calculate Square`() {
    assertEquals(25, MathUtils.square(5))
    assertEquals(36, MathUtils.square(6))
}

We can directly access the square() function from our util class without any extra steps.

3.1. Util Class With Extension Functions

Likewise, util classes in Kotlin often incorporate extension functions, a feature that allows developers to augment existing classes with new functionalities without directly modifying their source code.

Now, let’s create a util class that contains an extension function:

object ExtensionUtil {
    fun String.isPalindrome(): Boolean {
        val cleanString = this.replace("\\s".toRegex(), "").toLowerCase()
        return cleanString == cleanString.reversed()
    }
}

In this code, the isPalindrome() function is an extension function for the String class. The function returns a Boolean indicating whether the string is a palindrome or not.

Let’s take a look at the test for our ExtensionUtil class:

@Test
fun `Should Check If Palindrome`() {
    assertTrue("radar".isPalindrome())
    assertTrue("A man a plan a canal Panama".isPalindrome())
}

We can directly use this function from our util class again, with the only difference being that we must call it from a String instance.

The reusability aspect of these extension functions is paramount, as it allows these functions to be accessed across various sections of our code, embodying the DRY (Don’t Repeat Yourself) principle. Once defined within a util class, these extension functions can be used wherever the associated type is utilized, promoting efficiency and consistency in code implementation.

Furthermore, util classes contribute to encapsulation by providing a controlled environment for extension functions. This encapsulation also simplifies the management and updating of functionalities associated with specific types. Aligning with Kotlin’s emphasis on expressive and concise code, this encapsulated design ensures that extensions are logically grouped, enhancing code readability and maintainability throughout our project.

4. Java Interoperability and @JvmStatic

When we’re working with Kotlin and want our code to be interoperable with Java, we might encounter scenarios where we need to use the @JvmStatic annotation within util classes. This annotation is particularly useful when defining methods in a Kotlin object or companion object that we intend to call from Java code.

Here’s an example to illustrate the usage of @JvmStatic:

object Utils {
    @JvmStatic
    fun foo(): Boolean {
        return false
    }
}

Specifically, we’ll look at how we can call our util function from Java if it doesn’t have the @JvmStatic annotation:

Utils.INSTANCE.foo();

Significantly, the Kotlin compiler generates a static property named INSTANCE to hold the singleton util object. Therefore, without the @JvmStatic annotation, we can’t directly access our foo() function from the Utils class — we must go through the instance.

Finally, let’s call our util function from Java as if it were annotated with the @JvmStatic annotation:

Utils.foo();

The @JvmStatic annotation generates a proper static method in the Java bytecode. This means we can call foo() directly from Utils, without the need to first reference the instance property.

If we intend to use our util class from both Kotlin and Java, then the @JvmStatic annotation simplifies the interop to Java and lets the code behave more like a typical Java static method.

5. Conclusion

Utility classes in Kotlin provide a powerful mechanism for encapsulating reusable functionalities, promoting code organization, and enhancing maintainability. By exploring various examples and writing corresponding tests, we’ve illustrated the versatility and importance of util classes in Kotlin development.

As usual, the full implementation of these examples is available over on GitHub.