Skip to content

Conversation

@DougGregor
Copy link
Member

@DougGregor DougGregor commented Oct 31, 2024

Java classes are currently translated in Swift structs, and we use is/as
functions to cast up and down the hierarchy. Start down an alternative
path where we translate Java classes into Swift classes, making proper use
of inheritance in Swift to reflect inheritance in Java.

This step updates the @javaclass macro implementation to check whether
it is being applied to a class (rather than a struct) and adjust its
behavior accordingly:

  • The superclass is extracted from the inheritance clause of the class
    rather than the "extends" argument to the macro.
  • The "full Java class name" static member becomes a "class" member.
    For everything other than JavaObject itself, it is an override.
  • The javaHolder property is only emitted into JavaObject, nowhere else.
  • The init(javaHolder:) initializer becomes required. For everything
    other than JavaObject, it's implemented as a super.init call.
  • The JavaSuperclass typealias is no longer emitted.

All of this is keyed off the subject of the attribute being a "class"
rather than a "struct", so we keep the existing struct versions working.

Relatedly, extend the translator with an option to map Java classes to
Swift classes. This involves a number of changes:

  • Emitting each as an "open class" rather than "public struct"
  • Emitting the superclass into the inheritance clause rather than the "extends" argument
  • Emitting methods as "open" rather than "public", and "override" when we are overriding a method declared in a superclass
  • Emitting initializers as "convenience" since we still always build a Java object behind the scenes
  • Only emit methods and fields declared in the class, not inherited ones

This option is only currently only enabled in test cases while we stage in this functionality.

This is for issue #132.

…ft classes

Java classes are currently translated in Swift structs, and we use is/as
functions to cast up and down the hierarchy. Start down an alternative
path where we translate Java classes into Swift classes, making proper use
of inheritance in Swift to reflect inheritance in Java.

This step updates the @javaclass macro implementation to check whether
it is being applied to a class (rather than a struct) and adjust its
behavior accordingly:

* The superclass is extracted from the inheritance clause of the class
rather than the "extends" argument to the macro.
* The "full Java class name" static member becomes a "class" member.
For everything other than JavaObject itself, it is an override.
* The `javaHolder` property is only emitted into JavaObject, nowhere else.
* The `init(javaHolder:)` initializer becomes required. For everything
other than JavaObject, it's implemented as a super.init call.
* The `JavaSuperclass` typealias is no longer emitted.

All of this is keyed off the subject of the attribute being a "class"
rather than a "struct", so we keep the existing struct versions working.

Relatedly, extend the translator with an option to map Java classes to
Swift classes. This involves a number of changes:

* Emitting each as an "open class" rather than "public struct"
* Emitting the superclass into the inheritance clause rather than the
"extends" argument
* Emitting methods as "open" rather than "public"
* Only emit methods and fields declared in the class, not inherited ones

This option is only currently only enabled in test cases while we stage
in this functionality.
…lass

When we import a class from Java into Swift, we check whether its superclass
was also imported before generating a reference to that superclass. If it
isn't there, we fell back to JavaObject. That's too conservative, because
there might be a superclass in between that we could use.

Instead, walk up the superclass chain until we find the most-specific
superclass that *is* mapped into Swift, and use that as the generated
superclass. Additionally, use this as the basis for determining when we
need the "override" keyword when in the class-generating mode.
No functionality changes here; we're just more consistent about putting
JavaObject in as the extended type.
We don't need this function when generating classes, because we'll already
get the subtype conversion for free.
@ktoso
Copy link
Collaborator

ktoso commented Oct 31, 2024

Hm, definitely promising direction. I like that the object holder is in the super class, that makes sense. open also makes sense though maybe I'm unnecessarily worried about actually using this ability to subclass those 😅

So far looking good :)

When looking for a declared method with a given signature, we need to
check all superclasses that have been mapped into Swift, not just the
closest one.
Swift doesn't have the notion of "protected", so treat these as "open"
(or "public") as approriate.
Keep interfaces as structs for now. Their future is yet unwritten.
…va and Swift

Java allows more subtyping relationships for the result types in a covariant
method override than Swift does, such as covariant arrays and wildcards. Take
the Swift semantics into account when determining whether to apply the
`override` keyword.
Swift has inheritance of initializers, while Java does not have inheritance
of constructors. This means that Swift's attempt to treat a convenience
initializer as an override within a subclass can cause problems with
mismatched signatures. Use `@_nonoverride` to avoid this problem.
"""
@JavaMethod
public convenience init(environment: JNIEnvironment? = nil)
@_nonoverride public convenience init(environment: JNIEnvironment? = nil)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hah sneaky 👍

@ktoso
Copy link
Collaborator

ktoso commented Oct 31, 2024

Looks good 👍

@DougGregor
Copy link
Member Author

Merging this one. Aside from bringing in another Java class or two into JavaKit, it doesn't affect the current behavior.

@DougGregor DougGregor merged commit 37bce97 into swiftlang:main Oct 31, 2024
11 checks passed
@DougGregor DougGregor deleted the java-classes-to-swift-classes branch October 31, 2024 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants