Calling a generic Java varargs method from Kotlin - kotlin

I am trying to call the following Java method from Kotlin:
KStream<K, V>[] branch(final Predicate<? super K, ? super V>... predicates);
This method is part of the Kafka Streams API.
The way I tried to call it, following the Kotlin docs, was by defining an extension method:
fun <K, V> KStream<K, V>.kbranch(vararg predicates: (K, V) -> Boolean):
Array<KStream<K, V>> = this.branch(*predicates)
The problem is that it doesn't compile. I get the following error:
Type mismatch: inferred type is Array<out (K, V) -> Boolean>
but Array<(out) Predicate<in K!, in V!>!>! was expected
The similar KStream#filter method, which accepts a single Predicate as an argument, can be called without any issue when passing a Kotlin lambda with the same signature of (K, V) -> Boolean.
Any ideas? I have seen this similar question, but something here seems subtly different and I can't pinpoint it.

Unfortunately SAM conversion is not supported for variable length arguments. You can work around it though by converting the (K, V) -> Boolean to Predicate<K,V> yourself like so:
fun <K, V> KStream<K, V>.kbranch(vararg predicates: (K, V) -> Boolean): Array<KStream<K, V>> {
val arguments = predicates.map { Predicate { key: K, value: V -> it(key, value) } }
return this.branch(*arguments.toTypedArray())
}

Actually you can define like this.
fun <in K, in V> KStream<K, V>.kbranch(vararg predicates: out Predicate<K, V>):
Array<KStream<K, V>> = this.branch(*predicates)

Related

Type inference failed: Not enough information to infer parameter T

I have a function that compares responses from two different endpoints. It looks like this:
suspend fun <I, T> multiplexOrShadow(
request: I,
v1ResponseStringGenerator: KFunction1<T, String> = ::getV1ResponseString,
v2ResponseStringGenerator: KFunction1<T, String> = ::getV2ResponseString,
) {
<Call two different endpoints for request>
val v1str = v1ResponseStringGenerator(v1Response)
val v2str = v2ResponseStringGenerator(v2Response)
<compare v1str and v2str>
}
As you can see, the caller can pass in the function on how to generate response string from the response of the two endpoints. I also have a default function for the response generators. They look like this:
private fun <T> getV1ResponseString(v1Response: T): String {
return v1Response.toString()
}
private fun <T> getV2ResponseString(v2Response: T): String {
return v2Response.toString()
}
This compiles fine in IntelliJ. However, when I run the gradle build, it fails with the error
Type inference failed: Not enough information to infer parameter T in fun <T> getV1ResponseString(v1Response: T): String
Please specify it explicitly.
What am I doing wrong? I am using Kotlin 1.6.10 in my gradle build.
Looks like this is a known issue, which is fixed since Kotlin 1.6.20: https://youtrack.jetbrains.com/issue/KT-12963.
For Kotlin 1.6.10, the workaround is to avoid using the KFunctionN types if you don't need them. For example, if you only need to invoke a function, it's fine to use just the FunctionN type, also denoted with (...) -> ...:
suspend fun <I, T> multiplexOrShadow(
request: I,
v1ResponseStringGenerator: (T) -> String = ::getV1ResponseString,
v2ResponseStringGenerator: (T) -> String = ::getV2ResponseString,
) {
...
}

How to impose generic constraints to the parameters and return values of a function argument of a generic Kotlin function?

I want to define a Kotlin generic function that takes a lambda as a parameter, but I want to restrict the allowed types of lambdas parameters and return types. How do I do this in Kotlin?
In the following example, I expected the constraint: where T: Base, V: (T) -> Unit to mean that V can only be functions whose first parameter implements Base.
However, I see that the compiler ignores the T: Base part of the constraint and will accept any (Any) -> Unit.
interface Base
fun <T, V> exampleGenericFunction(func: V) where T: Base, V: (T) -> Unit {
println("func is $func")
}
class ImplementsBase : Base
class DoesNotImplementBase
fun main() {
val f1: (ImplementsBase) -> Unit = { }
exampleGenericFunction(f1)
val f2: (DoesNotImplementBase) -> Unit = { }
exampleGenericFunction(f2) // expected this to be a compilation error
}
This unexpected behavior was caused by a bug in the Kotlin >=1.4 compiler.
This bug can be tracked here: https://youtrack.jetbrains.com/issue/KT-48935.

Why generic form of groupBy in Kotlin is not enough?

Kotlin documentation for groupBy shows special form for every type, like ByteArray, IntArray. Why it is so, why the single generic form is not enough?
inline fun <T, K> Array<out T>.groupBy(
keySelector: (T) -> K
): Map<K, List<T>>
The snippet from Kotlin documentation
inline fun <T, K> Array<out T>.groupBy(
keySelector: (T) -> K
): Map<K, List<T>>
inline fun <K> ByteArray.groupBy(
keySelector: (Byte) -> K
): Map<K, List<Byte>>
inline fun <K> ShortArray.groupBy(
keySelector: (Short) -> K
): Map<K, List<Short>>
inline fun <K> IntArray.groupBy(
keySelector: (Int) -> K
): Map<K, List<Int>>
...
Question 2
It seems like IntArray is not subclassing the Array and that's probably the reason why it is necessary.
So, I wonder - if I would like to add my own function, let's say verySpecialGroupBy - does it means that I would also need to specify not just one such function - but repeat it for every array type?
Or it's a very specific and rare case when you would need to use those special arrays and in practice you can just define your function for generic Array and ignore the rest?
Those array specializations are for arrays of primitive types. So in your example of creating a verySpecialGroupBy function, you would only need to repeat it for each specialization if you wanted to use it with primitive type arrays.
You can read more about the need of primitive type array in this Kotlin discussion thread.

Add Extension function in kotlin to all classes

Is it possible to add extension function to all classes? I was thinking about adding it to some common base class like Object. Is it possible?
With Kotlin, Any is the super type like Object for Java.
fun Any.myExtensionFunction() {
// ...
}
And if you want to support null-receiver too:
fun Any?.myExtensionFunction() {
// ...
}
It depends on whether you want to use the type of the object in the signature (either in another argument, or in the return type). If not, use Any(?) as Kevin Robatel's answer says; but if you do, you need to use generics, e.g. (from the standard library)
inline fun <T, R> T.run(block: T.() -> R): R
inline fun <T> T.takeIf(predicate: (T) -> Boolean): T?
etc.

In Kotlin, how do I idiomatically access nullable nested map values, or return a default?

Quick Kotlin best practices question, as I couldn't really work out the best way to do this from the documentation.
Assume I have the following nested map (typing specified explicitly for the purpose of this question):
val userWidgetCount: Map<String, Map<String, Int>> = mapOf(
"rikbrown" to mapOf(
"widgetTypeA" to 1,
"widgetTypeB" to 2))
Can the following mode be any more succinct?
fun getUserWidgetCount(username: String, widgetType: String): Int {
return userWidgetCount[username]?.get(widgetType)?:0
}
In other words, I want to return the user widget count iff the user is known and they have an entry for that widget type, otherwise zero. In particular I saw I can use [] syntax to access the map initially, but I couldn't see a way to do this at the second level after using ?..
I would use an extension operator method for that.
// Option 1
operator fun <K, V> Map<K, V>?.get(key: K) = this?.get(key)
// Option 2
operator fun <K, K2, V> Map<K, Map<K2, V>>.get(key1: K, key2: K2): V? = get(key1)?.get(key2)
Option 1:
Define an extension that provides get operator for nullable map. In Kotlin's stdlib such approach appears with Any?.toString() extension method.
fun getUserWidgetCount(username: String, widgetType: String): Int {
return userWidgetCount[username][widgetType] ?: 0
}
Option 2:
Create a special extension for map of maps. In my opinion, it is better because it shows the contract of the map of maps better than two gets in a row.
fun getUserWidgetCount(username: String, widgetType: String): Int {
return userWidgetCount[username, widgetType] ?: 0
}