Common String Extension Methods in Kotlin

Kotlin is a modern programming language that offers many features to make coding easier and more enjoyable. One of these features is the ability to extend a class or an interface with new functionality without having to inherit from it or use design patterns such as Decorator. This is done via special declarations called extension functions.

Extension functions are useful when you want to add some functionality to a class that you can’t modify, such as a class from a third-party library. For example, you can write new functions for the String class that can be used as if they were methods of the String class.

In this blog post, we will explore some common extension functions that are available in Kotlin for the String class. We will also see how to write our own extension functions and how they are resolved at compile-time.

Built-in Extension Functions for String Class

The String class in Kotlin comes with a huge number of extension functions that can make working with strings easier and more concise. Here are some examples of these functions:

  • replace(oldChar: Char, newChar: Char, ignoreCase: Boolean = false): String – This function returns a new string with all the occurrences of the oldChar replaced with newChar. The ignoreCase parameter is false by default. If set to true, it will ignore case while handling the replacement. For example:

val inputString = "Jelly" println(inputString.replace('l', 'z')) // Jezzy println(inputString.replace('j', 'P', true)) // Pelly println(inputString.replace('j', 'P')) // Jelly

  • uppercase(): String – This function returns a new string with all the characters converted to uppercase. For example:

val inputString = "hello" println(inputString.uppercase()) // HELLO

  • lowercase(): String – This function returns a new string with all the characters converted to lowercase. For example:

val inputString = "HELLO" println(inputString.lowercase()) // hello

  • toCharArray(): CharArray – This function returns a char array containing the characters of the string. For example:

val inputString = "hello" val charArray = inputString.toCharArray() println(charArray.joinToString()) // h, e, l, l, o

  • substring(startIndex: Int, endIndex: Int): String – This function returns a new string that is a substring of the original string starting from startIndex (inclusive) and ending at endIndex (exclusive). For example:

val inputString = "hello" println(inputString.substring(1, 4)) // ell

  • startsWith(prefix: String, ignoreCase: Boolean = false): Boolean – This function returns true if the string starts with the specified prefix. The ignoreCase parameter is false by default. If set to true, it will ignore case while checking for the prefix. For example:

val inputString = "hello" println(inputString.startsWith("he")) // true println(inputString.startsWith("He", true)) // true println(inputString.startsWith("He")) // false

  • endsWith(suffix: String, ignoreCase: Boolean = false): Boolean – This function returns true if the string ends with the specified suffix. The ignoreCase parameter is false by default. If set to true, it will ignore case while checking for the suffix. For example:

val inputString = "hello" println(inputString.endsWith("lo")) // true println(inputString.endsWith("Lo", true)) // true println(inputString.endsWith("Lo")) // false

  • compareTo(other: String, ignoreCase: Boolean = false): Int – This function compares the string with another string lexicographically and returns an integer value. The value is negative if the string is less than the other string, zero if they are equal, and positive if the string is greater than the other string. The ignoreCase parameter is false by default. If set to true, it will ignore case while comparing the strings. For example:

val inputString = "hello" println(inputString.compareTo("world")) // -15 println(inputString.compareTo("Hello", true)) // 0 println(inputString.compareTo("Hello")) // 32

There are many more extension functions for the String class in Kotlin that you can explore in the official documentation.

Writing Your Own Extension Functions for String Class

You can also write your own extension functions for the String class or any other class in Kotlin. To declare an extension function, you need to prefix its name with a receiver type, which refers to the type being extended. For example, the following adds a reverse() function to the String class:

fun String.reverse(): String { return this.reversed() }

The this keyword inside an extension function corresponds to the receiver object (the one that is passed before the dot). Now, you can call such a function on any String object in Kotlin:

val inputString = "hello" println(inputString.reverse()) // olleh

You can also make your extension functions generic by declaring the type parameter before the function name. For example, the following adds a swap() function to the MutableList class that can swap two elements of any type:

fun <T> MutableList<T>.swap(index1: Int, index2: Int) { val tmp = this[index1] this[index1] = this[index2] this[index2] = tmp } val list = mutableListOf(1, 2, 3) list.swap(0, 2) println(list) // [3, 2, 1]

How Extension Functions Are Resolved

It is important to understand that extension functions do not actually modify the classes they extend. They are just syntactic sugar that allows you to call new functions with the dot-notation on variables of that type. Extension functions are resolved statically at compile-time, which means they are not virtual by receiver type. The extension function being called is determined by the type of the expression on which the function is invoked, not by the type of the result from evaluating that expression at runtime.

For example, consider the following code:

open class Shape class Rectangle: Shape() fun Shape.getName() = "Shape" fun Rectangle.getName() = "Rectangle" fun printClassName(s: Shape) { println(s.getName()) } printClassName(Rectangle())

This code prints Shape, because the extension function called depends only on the declared type of the parameter s, which is the Shape class. The actual type of s at runtime (Rectangle) does not matter.

If a class has a member function with the same name and signature as an extension function, the member always wins. For example:

class Example { fun printFunctionType() { println("Class method") } } fun Example.printFunctionType() { println("Extension function") } Example().printFunctionType() // Class method

This code prints Class method, because the member function of the Example class overrides the extension function.

Conclusion

In this blog post, we have learned about some common extension functions for the String class in Kotlin and how to write our own extension functions for any class. We have also seen how extension functions are resolved at compile-time and how they do not modify the classes they extend.

Extension functions are a powerful feature of Kotlin that can help you write more concise and expressive code. They can also help you extend existing classes with new functionality without having to inherit from them or use design patterns such as Decorator.

If you want to learn more about extension functions and other features of Kotlin, you can check out these resources:

Author: John Rowan

I am a Senior Android Engineer and I love everything to do with computers. My specialty is Android programming but I actually love to code in any language specifically learning new things.

Author: John Rowan

I am a Senior Android Engineer and I love everything to do with computers. My specialty is Android programming but I actually love to code in any language specifically learning new things.

%d bloggers like this: