Home > front end >  Extension function from a generic interface
Extension function from a generic interface

Time:09-17

Consider the following interface

interface EntityConverter<in A, out B> {
    fun A.convert(): B

    fun List<A>.convert(): List<B> = this.map { it.convert() }
}

I want to use it in a spring boot application where specific implementations get injected so that the extension function becomes usable on the type.

However this doesn't work. The compiler does not resolve the extension function.

CodePudding user response:

Note that you're defining extension functions that are also member functions of the EntityConverter type. You should take a look at this part of the doc for information about how this works.

Essentially, in order to use them, you need 2 instances in scope:

  • the dispatch receiver (an instance of EntityConverter<A, B>)
  • the extension receiver (an instance of A or List<A>, where A matches the first type parameter of the EntityConverter in scope)

You can use with() to bring the EntityConverter in scope so you can use convert on your other instances using the usual . syntax:

val converter = object : EntityConverter<Int, String> {
    override fun Int.convert() = "#$this"
}

val list = listOf(1, 2, 3)

val convertedList = with(converter) {
    list.convert()
}
println(convertedList) // prints [#1, #2, #3]

Now you have to decide whether this kind of usage pattern is what makes most sense for your use case. If you'd prefer more "classic" calls without extensions (converter.convert(a) returning a B), you can declare your functions as regular methods taking an argument instead of a receiver.

Bonus: functional interface

As a side note, if you add the fun keyword in front of your EntityConverter interface, you can create instances of it very easily like this:

val converter = EntityConverter<Int, String> { "#$this" }

This is because your converter interface only has a single abstract method, making it easy to implement with a single lambda. See the docs about functional interfaces.

CodePudding user response:

I'm not sure if you can mention extension functions as a part of interface, because it's like static functions.

I'd recommend to put "common" function in interface with A typed parameter. Then just put extension method for list nearby.

interface EntityConverter<in A, out B> {
    fun convert(a: A): B    
}

fun <A, B> EntityConverter<A, B>.convert(list: List<A>): List<B> = list.map { convert(it) }

Update

I wasn't aware about possibility of inheritance of extension methods in Kotlin. And about its overriding as well. So my answer could be just an alternative of using extension methods.

  • Related