Home > Back-end >  Kotlin constrain generic method's type parameter to be a supertype of class's type paramet
Kotlin constrain generic method's type parameter to be a supertype of class's type paramet

Time:08-10

I'm trying to get a class similar to this (contrived) example to compile:

class Foo<T> {
    
    val value: T
    val condition: Boolean
    
    fun <R> transform(func: () -> R): R {
        return if (condition) {
            func()
        } else {
            // Type mismatch: required: R, found: T
            value
        }
    }
    
}

The transform method can return either value or the result of func(), so T should be assignable to R. Is there a way to express this in Kotlin?

I tried using where T : R, but the compiler doesn't understand that T should refer to the class's T. An extension function could work, but I want to avoid that because it complicates Java interoperability.

CodePudding user response:

You can try this , it works. You need to pass two type params while initializing.

class Foo<T:R,R> constructor(val value: T,val condition: Boolean) {
    fun transform(func: () -> R): R {
        return if (condition) {
            func()
        } else {
            value
        }
    }
}

Example:

enter image description here

  var s = Foo<String,CharSequence>("12",false).transform {
        "as"
    }

You pass "12" as string . Transform return "as" value as CharSequence .

Update: As far as I know, only using extension function might be solve your requirement.

Here is the extension function solution.

class Foo<T> constructor(val value: T,val condition: Boolean){}
fun <T:R,R> Foo<T>.transform(func: () -> R):R{
    return if (condition) {
        func()
    } else {
        value
    }
}

Example of using extension function solution.

fun main() {
    var s1 = Foo(setOf("Hello"),false).transform<Set<String>,Iterable<String>> {
        setOf("World")
    }
    var s2 = Foo(listOf("Hello"),false).transform<List<String>,Iterable<String>> {
        listOf("World")
    }
}

enter image description here

CodePudding user response:

You just use the type parameter from the class directly. Your method doesn't need to introduce another type parameter:

class Foo<T> {
    val value: T
    val condition: Boolean

    fun transform(func: () -> T): T {
        return if (condition) {
            func()
        } else {
            value
        }
    }
}
  • Related