Kotlin - Overload resolution ambiguity. All these functions match - kotlin

I am trying to add a global state store to my topology, but getting error as
Overload resolution ambiguity. All these functions match.
val stateStoreBuilder =
Stores.keyValueStoreBuilder(
Stores.persistentKeyValueStore("my_global_store"),
Serdes.String(),
Serdes.String()
)
topology.addGlobalStore(
stateStoreBuilder,
"source_name",
KEY_JSON_DE,
VALUE_JSON_DE,
"topic_name",
"processor_name",
{ MyStoreProcessor::class.java },
)
Getting error for addGlobalStore method.
Using below versions in Gradle file :-
kotlin("jvm") version "1.7.10"
kotlin("plugin.spring") version "1.7.10"
implementation("org.apache.kafka:kafka-streams:3.3.1")
implementation("org.springframework.kafka:spring-kafka")

I think the problem is shows in the small letter of the error.
The Kotlin compiler cannot figure out which method to use. In particular to what class it should map the last lambda, to org.apache.kafka.streams.processor.ProcessSupplier or org.apache.kafka.streams.processor.api.ProcessSupplier (notice the later has an api package in the middle).
I reckon that if you cast the lambda, it should work. I've never tried this, but I wonder if changing the line to the following would work
{ MyStoreProcessor::class.java } as org.apache.kafka.streams.processor.api.ProcessSupplier

Related

Why cannot be `const val` used in build.gradle.kts

I'd like to define a version constant in guild.gradle.kts file so that it can be used even in the plugins block. The plugins block requires restricted syntax:
«plugin version» must be constant, literal, strings
Following the restriction I tried to define the a version constant:
const val kotlinVersion = "1.3.72"
plugins {
java
kotlin("jvm") version kotlinVersion
}
However this fails with message
Const 'val' are only allowed on top level or in objects
even though the declaration seem to meet all const requirements. Why cannot be const val used in build.gradle.kts?
Even though it seems like your build script is top level, it isn't. The gradle docs mentions this when explaining the lifecycle of the build:
Finally, evaluate each Project by executing its build.gradle file, if present, against the project.
(source) This means that in your kotlin build scripts, the receiver type (i.e. this) is KotlinBuildScript which is eventually a subclass of Project. I don't know the details about how it's evaluated, but I can imagine it would be equivalent to what you can do in Kotlin with receiver types:
fun Project.evaluate(buildScript: Project.() -> Unit) = this.evaluate()
So your build script is really just the inside of a closure, hence why you can't use const val.

Suppress DeprecationLevel.ERROR in Kotlin

I’m using DeprecationLevel.ERROR in my APIs:
#Deprecated(
message = "moved to def()",
replaceWith = ReplaceWith(expression = "def()"),
level = DeprecationLevel.ERROR)
fun abc() = def()
I want a test to ensure callers see this replacement. For example, my test should fail to compile if I accidentally delete the abc() method.
But I can’t find a way to make this compile:
#Test
#Suppress("something")
fun deprecatedAbc() {
abc()
}
For example, #Suppress("DEPRECATION") doesn’t work. Is there something that does?
According to DefaultErrorMessages, you can use #Suppress("DEPRECATION_ERROR").
According to the documentation of #Deprecated:
To help removing deprecated API gradually, the property level could be used. Usually a gradual phase-out goes through the "warning", then "error", then "hidden" or "removed" stages:
First and by default, DeprecationLevel.WARNING is used to notify API consumers, but not to break their compilation or runtime usages.
Then, some time later the deprecation level is raised to DeprecationLevel.ERROR, so that no new Kotlin code can be compiled using the deprecated API.
Code does not compile by design -- and #Suppress only suppresses warnings, not errors (see doc).
So, what you are trying to achieve is not possible. Also, it seems to me as if you're testing a Kotlin feature, rather than your business logic. You might instead try to test the behavior of the replacement.

Unable to reference kotlinx.cinterop package in Kotlin Multiplatform project

I created a Kotlin Multiplatform project based on the example for iOS and Android (https://kotlinlang.org/docs/tutorials/native/mpp-ios-android.html). Within my iOS target, I am using cinterop to link in a 3rd party Objective-C framework. This is working fine. I am able to reference the framework classes in my Kotlin code within the 'iosMain' source set. However, one of the method calls requires an NSError** parameter. I am attempting to use the following to create this variable in my Kotlin code:
kotlinx.cinterop.NativePlacement.allocPointerTo<kotlinx.cinterop.ObjCObjectVar<platform.Foundation.NSError?>()
I have not been able to find away around the following errors:
> Task :sharedLib:linkMainDebugFrameworkIOS FAILED
src/iosMain/kotlin/Platform.kt:9:50: error: unresolved reference: allocPointerTo
val error = kotlinx.cinterop.NativePlacement.allocPointerTo<kotlinx.cinterop.ObjCObjectVar<platform.Foundation.NSError?>()
^
src/iosMain/kotlin/Platform.kt:9:126: error: no value passed for parameter 'rawPtr'
val error = kotlinx.cinterop.NativePlacement.allocPointerTo<kotlinx.cinterop.ObjCObjectVar<platform.Foundation.NSError?>()
My understanding is that this package is part of stdlib, which should be added as a dependency automatically. Am I missing a piece of the equation somewhere?
UPDATE
The following is an example of passing an NSError* reference from Kotlin:
memScoped {
val errorRef = alloc<ObjCObjectVar<NSError?>>()
someObjCObject.method(errorRef.ptr)
}
kotlinx.cinterop.NativePlacement is an interface name, not a class name. You need an instance of the NativePlacement interface to work. For example memscoped{..} block will work, e.g.
import kotlinx.cinterop.*
import platform.Foundation.*
val p = memScoped {
allocPointerTo<ObjCObjectVar<NSError?>>()
}
The memscoped{..} accepts a lambda, inside the lambda the receiver implements the NativePlacement type, so there is no need for a qualifier to call a function on it
https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver

Jinq in Kotlin - how to convert lambda into java SerializedLambda?

Can I have serializable lambda in Kotlin? I am trying to use Jinq library from Kotlin, but it requires serializable lambdas. Is there any syntax that makes it possible?
Update:
My code:
var temp=anyDao.streamAll(Task::class.java)
.where<Exception,Task> { t->t.taskStatus== TaskStatus.accepted }
.collect(Collectors.toList<Task>());
I am getting this error:
Caused by: java.lang.IllegalArgumentException:
Could not extract code from lambda.
This error sometimes occurs because your lambda references objects that aren't Serializable.
All objects referenced in lambda are serializable (code results in no errors in java).
Update 2
After debugging it seems that kotlin lambda isn't translated into java.lang.invoke.SerializedLambda which is required by Jinq to get information from. So the problem is how to convert it to SerializedLambda.
I'm the maker of Jinq. I haven't had the time to look at Kotlin-support, but based on your description, I'm assuming that Kotlin compiles its lambdas into actual classes or something else. As such, Jinq would probably need some special code for cracking open Kotlin lambdas, and it may also need special code for handling any unusual Kotlin-isms in the generated code. Jinq should be capable of handling it because it was previously retrofitted to handle Scala lambdas.
If you file an issue in the Jinq github about it, along with a small Kotlin example (in both source and .class file form), then I can take a quick peek at what might be involved. If it's small, I can make those changes. Unfortunately, if it looks like a lot of work, I don't think I can really justify putting a lot of resources into adding Kotlin support to Jinq.
I have no experience on Jinq, but according to the implementation in GitHub and my experience of using Java Library in Kotlin.
ref: https://github.com/my2iu/Jinq/blob/master/api/src/org/jinq/orm/stream/JinqStream.java
You can always fall back to use the native Java Interface in Kotlin.
var temp = anyDao.streamAll(Task::class.java)
.where( JinqStream.Where<Task,Exception> { t -> t.taskStatus == TaskStatus.accepted } )
.collect(Collectors.toList<Task>());
// Alternatively, You you can import the interface first
import org.jinq.orm.stream.JinqStream.*
...
// then you can use Where instead of JinqStream.Where
var temp = anyDao.streamAll(Task::class.java)
.where(Where<Task,Exception> { t -> t.taskStatus == TaskStatus.accepted } )
.collect(Collectors.toList<Task>());
Or make a custom extension to wrap the implementation
fun JinqStream<T>.where(f: (T) -> Boolean): JinqStream<T> {
return this.where(JinqStream.Where<T,Exception> { f(it) })
}
Disclaimer: The above codes have not been tested.

Kotlin overload resolution ambiguity in the standard library

In Kotlin 0.12.1230 it appears that deprecated APIs are blocking the use of their replacements.
For example, the compiler complains about the following snippet because it "cannot choose among... candidates without completing type inference"
val seq = sequenceOf("1")
val first = seq.firstOrNull()
The candidates are Sequence<T>.firstOrNull and Stream<T>.firstOrNull both of which have identical signatures and Sequence<T> extends Stream<T>. Furthermore, Stream<T> is deprecated in favor of Sequence<T>.
Attempting to resolve the type inference ambiguity, like you see below, results in the compiler complaining about "overload resolution ambiguity".
val seq = sequenceOf("1")
val first = seq.firstOrNull<String?>()
Is there any way to resolve the ambiguity while we wait for deprecated APIs to disappear entirely?
It seems that casting to the least specific type, in this case the deprecated type Stream<T>, accomplishes it, but now my code explicitly depends on a deprecated type when I have no desire to do so:
val seq = sequenceOf("1")
val first = (seq as Stream<String>).firstOrNull()
Hopefully there is a better way?
This seems to be caused by multiple conflicting versions of the Kotlin stdlib on my classpath (caused by a long standing defect in Gradle IntelliJ integration). Once they were version conflict resolved, the compiler no longer complains.