Kotlin

Kotlin: Type conversion with adapters

In this post we will learn how we can use Kotlin extension functions to provide a simple and elegant type conversion mechanism.

Maybe you have used Apache Sling before. In this case, you are probably familiar with Slings usage of adapters. We will implement a very similar approach in Kotlin.

Creating an extension function

With Kotlins extension functions we can add methods to existing classes. The following declaration adds an adaptTo(..) method to all sub types of Any.

1
2
3
fun <F : Any, T : Any> F.adaptTo(targetType: KClass<T>): T {
    ..
}

The passed targetType parameter specifies the target type that should be returned by the method. We keep the method body empty for the moment.

Converting an Object of type A to another object of type B will look like this with our new method:

1
2
val a: A = A()
val b: B = a.adaptTo(B::class)

Providing conversion rules with adapters

In order to implement the adaptTo(..) method we need a way to define conversion rules.

We use a simple Adapter interface for this:

1
2
3
4
interface Adapter {
    fun <T : Any> canAdapt(from: Any, to: KClass<T>): Boolean
    fun <T : Any> adaptTo(from: Any, to: KClass<T>): T
}

canAdapt(..) returns true when the implementing class is able to convert the from object to type to.

adaptTo(..) performs the actual conversion and returns an object of type to.

Searching for an appropriate adapter

Our adaptTo(..) extension function needs a way to access available adapters. So, we create a simple list that stores our adapter implementations:

1
val adapters = mutableListOf<Adapter>()

Within the extension function we can now search the adapters list for a suitable adapter:

1
2
3
4
5
6
7
8
fun <F : Any, T : Any> F.adaptTo(targetType: KClass<T>): T {
    val adapter = adapters.find { it.canAdapt(this, targetType) }
            ?: throw NoSuitableAdapterFoundException(this, targetType)
    return adapter.adaptTo(this, targetType)
}
 
class NoSuitableAdapterFoundException(from: Any, to: KClass<*>)
    : Exception("No suitable adapter found to convert $from to type $to")

If an adapter is found that can be used for the requested conversion we call adaptTo(..) of the adapter and return the result. In case no suitable adapter is found a NoSuitableAdapterFoundException is thrown.

Example usage

Assume we want to convert JSON strings to Kotlin objects using the Jackson JSON library. A simple adapter might look like this:

01
02
03
04
05
06
07
08
09
10
class JsonToObjectAdapter : Adapter {
    private val objectMapper = ObjectMapper().registerModule(KotlinModule())
 
    override fun <T : Any> canAdapt(from: Any, to: KClass<T>) = from is String
 
    override fun <T : Any> adaptTo(from: Any, to: KClass<T>): T {
        require(canAdapt(from, to))
        return objectMapper.readValue(from as String, to.java)
    }
}

Now we can use our new extension method to convert a JSON string to a Person object:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
data class Person(val name: String, val age: Int)
 
fun main() {
    // register available adapter at application start
    adapters.add(JsonToObjectAdapter())
 
    ...
     
    // actual usage
    val json = """
        {
            "name""John",
            "age" 42
        }
    """.trimIndent()
 
    val person = json.adaptTo(Person::class)
}

You can find the source code of the examples on GitHub.

Within adapters.kt you find all the required pieces in case you want to try this on your own. In example-usage.kt you find some adapter implementations and usage examples.

Published on Java Code Geeks with permission by Michael Scharhag, partner at our JCG program. See the original article here: Kotlin: Type conversion with adapters

Opinions expressed by Java Code Geeks contributors are their own.

Michael Scharhag

Michael Scharhag is a Java Developer, Blogger and technology enthusiast. Particularly interested in Java related technologies including Java EE, Spring, Groovy and Grails.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Jolan
Jolan
2 years ago

using inline and reified types you can also create something like a.adapTo<B>(), which is more kotlin-esque and has as bonus that it can be inferred:
val b: B = a.adaptTo()

Nick
Nick
2 years ago
Reply to  Jolan

Yeah this whole post feels like Java dev realizing too late that Kotlin is eating Java alive… literally like a first piece of Kotlin code you write while still not being used to the language.

Back to top button