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
}
}
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With