xiang
xiang

Reputation: 1534

How to write lambdas with generics in kotlin?

I can write lambdas id_Int and id_Boolean with explicit type. And I can write function identity with type parameter. Can I write lambdas with type parameter?

fun testFuncInt(f: (Int) -> Int): Int = f(1) + 2

val id_Int = { x: Int -> x }

fun testFuncBoolean(f: (Boolean) -> Boolean): Boolean = !f(false)

val id_Boolean = { x: Boolean -> x }

fun <T> identity(x: T) = x

fun main(args: Array<String>) {
    println(testFuncInt(id_Int))
    println(testFuncInt(::identity))
    println(testFuncBoolean(id_Boolean))
    println(testFuncBoolean(::identity))
}

Upvotes: 22

Views: 14226

Answers (3)

Andrej
Andrej

Reputation: 280

No, but you generally don't need to. A lambda has no declaration (which is kind of the point), so it's essentially an expression you can pass to a function, store in a val/var as you did when doing val id_Boolean = { x: Boolean -> x } but the type really gets worked out as it would in an expression.

The calls here will resolve to the correct type, since your functions takes a function that takes an Int and returns an Int, and a function that takes a Boolean and returns a Boolean

testFuncInt({ x -> x }) // x is an Int
testFuncInt({ it })     // it is the default argument
testFuncInt { x -> x }  // as top one, Kotlin's syntactic sugar

The important thing here is that the lambda expression still offers type safety, i.e. if you did something like this

fun <T> foo(x: T, lambda: (T) -> Boolean): Boolean = lambda(x)
foo(42, { x -> x })   // oops, lambda takes a T and returns a T

this will trigger a compiler error as lambda's types don't match what foo expects, i.e. foo will pass an Int to the lambda and it expects a Boolean back. On the other hand, if you do this

foo(123, { it == 42 })
foo(123) { it == 42 }   // equivalent to above

The return type is actually deduced to be a Boolean as that's what the result of a comparison operation evaluates to and as 123 and 42 are of the same type, lambda's types actually fit into what foo expects.

Upvotes: 2

Kotlin does not have support for declaring generic properties without declaring that type at class level (see also), but you can do it using a function that returns a lambda corresponding to the desired type:

fun main(args: Array<String>) {
    println(testFuncBoolean(id()))
    println(testFuncInt(id()))
}

fun <T> id(): (T) -> T = { it }

fun testFuncInt(f: (Int) -> Int): Int = f(1) + 2
fun testFuncBoolean(f: (Boolean) -> Boolean): Boolean = !f(false)

Upvotes: 14

Sachin Chandil
Sachin Chandil

Reputation: 17809

You can not write lambda with generics, why following paragraph taken from official documentation says it all.

A lambda expression or an anonymous function is a "function literal", i.e. a function that is not declared, but passed immediately as an expression

A lambda expression or function is not declared, its an anonymous function.

But ultimately we do same thing by declaring function type as generic. We can pass a lambda expression that does the job.

fun testFuncInt(f: (Int) -> Int): Int = f(1) + 2 

you can call it like: testFuncInt{ a -> a } or testFuncInt{ it }

So finally you are doing same thing (lambdas with type parameter), but there is no term like that as lambdas are expressions or anonymous functions.

Hope it helps.

Upvotes: 2

Related Questions