Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin higher order function parameters: Passing subtypes

Tags:

kotlin

I have run into a problem with function parameters in Kotlin. I will explain the issue with the help of some code.

I created a class hierarchy. When I pass a subtype into a function expecting a parent type, there is no issue.

open class A (val i: Int)
class B (val j: Int) : A(j)

fun f(x: A){
    print(x)
}

fun test_f(){
    f(A(1))
    f(B(1)) //no problem
}

I tried to mimic this with function parameters.

fun g(x: (A)->Int){
    print(x)
}

fun test_g(){
    val l1 = { a: A -> a.hashCode()}
    g(l1)

    val l2 = { b: B -> b.hashCode()}
    g(l2) //Error: Type mismatch. Required: (A)->Int, Found: (B)->Int
}

It seems that function type (B) -> Int is not a subtype of (A) -> Int. What is the best way to address this?

My original problem is to define a higher order function in A.h that takes a function z: (A) -> X as parameter. And I want call h on an object of type B and pass a function z: (B) -> X.

Update: I tried generics with upper bound, yet my issue is not solved. Please find code below:

// Using generics doesn't allow me to pass A.
open class A (val i: Int) {
    fun <M: A> g(x: (M)->Int){
        print(x(this)) // Error: Type mismatch. Expected: M, Found: A 
    }
}
like image 248
Neo Avatar asked Apr 08 '19 11:04

Neo


People also ask

Can you pass functions as parameters in Kotlin?

In Kotlin, you can provide default values to parameters in function definition. If the function is called with arguments passed, those arguments are used as parameters.

Does Kotlin support higher-order functions?

Kotlin functions are first-class, which means they can be stored in variables and data structures, and can be passed as arguments to and returned from other higher-order functions.

Which of the following is a higher-order function in Kotlin?

In Kotlin, a function which can accept a function as parameter or can return a function is called Higher-Order function. Instead of Integer, String or Array as a parameter to function, we will pass anonymous function or lambdas.

How do you pass a list to a function in Kotlin?

Create a new List using the Kotlin standard library function listOf() , and pass in the elements of the list as arguments separated by commas. listOf(1, 2, 3, 4, 5, 6) returns a read-only list of integers from 1 through 6.


1 Answers

What you're trying to do is a conversion from function type (B) -> Int (source) to (A) -> Int (target). This is not a safe conversion.

Your source function (B) -> Int takes any instance which is a B, but not necessarily an instance of type A. More concretely, it cannot handle all arguments that are of type A but not of type B.

Imagine your classes look like this:

open class A
class B : A {
    fun onlyB() = 29
}

You can define a function

val fb: (B) -> Int = { it.onlyB() }
val fa: (A) -> Int = fb // ERROR

The function fb will not be able to operate on class A, since A does not have the onlyB() function. As a consequence, you're not allowed to convert it to a function type which takes A parameters.


This concept is called contravariance, meaning that input parameters can only be constrained by becoming more concrete, not more abstract. So, the opposite direction works:

val fa: (A) -> Int = { it.hashCode() }
val fb: (B) -> Int = fa // OK, every B is also an A

In contrast, for return values, the concept of covariance applies. This means that return values are allowed to become more abstract, but not more concrete:

val fInt: (B) -> Int = { it.onlyB() }
val fNum: (B) -> Number = fInt // OK, every Int is also a Number

These relations can be exploited in generic classes, using Kotlin's in (contravariance) and out (covariance) keywords -- see here for detailed explanation.

like image 118
TheOperator Avatar answered Sep 19 '22 11:09

TheOperator