Repository navigation
jextract: closure interfaces get a default invoke method for Kotlin - #953
piercifani wants to merge 2 commits into
Conversation
Closure interfaces declare one method, `apply`, so Kotlin code calls a closure value as `handler.apply(x)`. Kotlin treats a Java method named `invoke` as the invoke operator, so with `"closureMethodName": "invoke"` the same closure is called as `handler(x)`, and Kotlin lambdas still convert to the interface. The default stays `apply`. The name is used for the Java interface, the `@JavaInterface` printed for escaping closures and the call into Java, and the method looked up for non-escaping closures. With a name other than `apply`, closures that a `java.util.function` interface would cover, such as `(Int64) -> Int64` with `LongUnaryOperator.applyAsLong`, get their own interface too, so every closure is called the same way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
I think that's a good point. |
|
I wouldn't mind switching to calling it invoke by default if it happens to help kotlin interop. For specific interfaces we need we'd generate the specific names still. Could you look into that? |
|
alternatively we could generate a |
Replaces the `closureMethodName` option. Each closure interface jextract generates keeps its abstract `apply` method and adds a default `invoke` that calls it. Kotlin treats a method named `invoke` as the invoke operator, so a closure value is called as `handler(x)`, and Kotlin lambdas still convert to the interface since `apply` stays its only abstract method. The Swift side is unchanged: it still calls `apply`. Closures that a `java.util.function` interface covers keep that interface and its method. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks both! I went with the default method. Each generated closure interface keeps
The Happy to do any further tweaks to this! Cheers! |
Problem
A Swift closure crosses to Java as a functional interface with one method,
apply. From Kotlin, passing a lambda into Swift reads naturally, since Kotlin converts{ … }to the interface. Calling a closure value doesn't:Kotlin function types are called as
handler("hi"), and that's what Kotlin code expects.Changes
Each closure interface jextract generates in JNI mode keeps
applyand adds a defaultinvokemethod that calls it:Kotlin treats a Java method named
invokeas the invoke operator, so:handler("hi");applystays its only abstract method;apply.There's no new configuration option, and the Swift side is unchanged: it still calls
apply. Closures that ajava.util.functioninterface covers, such as(Int64) -> Int64withLongUnaryOperator, keep that interface and its method.Testing
JNIClosureInvokeTests(2 tests): interfaces with and without a result forwardinvoketoapply, ajava.util.functionclosure keeps its interface, and the Swift side still callsapply.JNIClosureTestsandJNIEscapingClosureTests.invokeash("x")and still converts a lambda to the interface.swift testand./gradlew :Samples:SwiftJavaExtractJNISampleApp:testpass.🤖 Generated with Claude Code