Repository navigation
JNI: optionalsAsNullable passes optionals as nullable Java types - #19
Open
piercifani wants to merge 3 commits into
Open
piercifani wants to merge 3 commits into
piercifani wants to merge 3 commits into
Conversation
With the new `optionalsAsNullable` option, a declaration's optional parameters and results use the wrapped Java type, boxed, with `null` for `nil`, instead of `Optional`/`OptionalLong`/`OptionalInt`/`OptionalDouble`. It's a Java-side layer over the existing conversions: a parameter builds the `Optional` they expect from the nullable argument, and a result unwraps their `Optional` once into a typed local. Native methods and Swift thunks are unchanged. Applied in `translate(_ decl:)` to the declaration's own optionals only; closure and collection element types keep `Optional`, and enum `getAsX()` wraps nullable associated values before mapping them. Off by default: it changes the Java API. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack 15/21 · base:
jni/14-default-arguments· previous: #18 · next: #20 (jni/16-closure-struct-arguments)Problem
Swift optionals reach Java as
Optional<T>,OptionalLong,OptionalIntorOptionalDouble. Kotlin can't use them asT?:val nick: String? = Greeter.nickname(id)doesn't compile.nullfornilisn't possible.Changes
A new configuration option,
optionalsAsNullable(defaultfalse), gives a declaration's optional parameters and results the wrapped Java type, boxed, withnullmeaningnil:String,java.lang.Long,Tag. It's opt-in because it changes the Java API of every optional.It's a thin Java-side layer over the existing optional conversions, so native signatures and the Swift thunks don't change:
Optionalit expects, built from the nullable argument:Optional.ofNullable(tag)or(count == null ? OptionalLong.empty() : OptionalLong.of(count)).Optionalgoes into a typed local,Optional<String> result$optional = …, and the method returnsresult$optional.isPresent() ? result$optional.get() : null, orgetAsLong()and friends for primitives.It's applied once per declaration, in
translate(_ decl:), to that declaration's own parameters and result. This covers functions, initializers, and property getters and setters.Nested signatures keep
Optional, because their Swift side passes anOptionalthrough JNI. That applies to closure parameters and results, and to the element types of arrays and other collections. EnumgetAsX()accessors keep returningOptional<Case.X>; their printer now wraps the nullable associated values withOptional.ofNullablebefore mapping them to the case record.Kotlin sees these as platform types (
String!), which can be used asString?. RealString?would need a nullability annotation Kotlin recognizes, which needs a dependency; that's left for later.Kotlin in this PR
NullablesTest.ktwithQuotes.swift; the library module turns the option on:Turning the option on in the module changes one earlier test:
JavaEnumsTestnow readsOrders.status("a")instead ofOrders.status("a").get().ClosuresTestis unchanged, since a closure's optional argument is still anOptional.Why Skip needs it
Skip turns Swift optionals into Kotlin nullable types, and the app's Kotlin code uses
?.,?:andnullwith them throughout. WithOptional, every one of those call sites would change.Testing
./gradlew :Samples:SwiftJavaExtractJNIKotlinSampleApp:testpasses (42 tests).JNINullableOptionalsTests(5 tests): a primitive parameter and aStringresult, with the unchanged native method; a primitive result; jextracted parameter and result; an escaping closure parameter keepsOptionalLong; the default output without the option.swift test --disable-experimental-prebuilts --disable-sandbox(673 tests) and./gradlew :Samples:SwiftJavaExtractJNISampleApp:test(293 tests) pass. The JNI sample doesn't turn the option on.swift run generate-config-docs --check(the option is documented);swift format lint --stricton changed files;git diff --check.Not in this PR
String?instead ofString!.🤖 Generated with Claude Code