Hey all,
I've put up a pull request to start translating Java classes into Swift classes. With this change, java.lang.BigInteger
imports like this:
@JavaClass("java.math.BigInteger")
open class BigInteger: JavaNumber {
@JavaMethod
public convenience init(_ arg0: String, environment: JNIEnvironment? = nil)
@JavaMethod
public convenience init(_ arg0: Int32, _ arg1: [Int8], _ arg2: Int32, _ arg3: Int32, environment: JNIEnvironment? = nil)
@JavaMethod
public convenience init(_ arg0: [Int8], environment: JNIEnvironment? = nil)
@JavaMethod
public convenience init(_ arg0: [Int8], _ arg1: Int32, _ arg2: Int32, environment: JNIEnvironment? = nil)
@JavaMethod
public convenience init(_ arg0: String, _ arg1: Int32, environment: JNIEnvironment? = nil)
@JavaMethod
public convenience init(_ arg0: Int32, _ arg1: [Int8], environment: JNIEnvironment? = nil)
@JavaMethod
open func bitCount() -> Int32
@JavaMethod
open override func equals(_ arg0: JavaObject?) -> Bool
@JavaMethod
open func toString(_ arg0: Int32) -> String
}
There are a couple of things to notice here:
- Java class inheritance is mapped into Swift class inheritance, e.g.,
BigInteger
inherits fromJavaNumber
rather than being buried in anextends:
argument of the macro. - The classes and methods are all
open
because they can be subclassed / overridden. We mark overrides as such in Swift. - Initializers are
convenience
because they all end up calling the Java constructor and then wrapping that up in C++.
There are a bunch of advantages to this new approach:
- Subtyping works! You can pass a
BigInteger
as aJavaNumber
orJavaObject
without annoying calls to.as(...)
. - Overrides are nicely documented with the
override
keyword - It (mostly) meets expectations: Swift classes are the closest semantic match for Java classes, and it comes across as odd (and if you're new to Swift--confusing) to have them mapped to structs that have value semantics
- We no longer need to emit all of the inherited methods into every class, because we get to inherit from the superclass. This reduced the size of the pre-generated sources within the swift-java package by about 2/3.
There are some challenges with this approach, too:
- The Swift
as?
andis
won't work reliably. They've never worked with swift-java, but the compiler would warn about their use. With this change, they'll work sometimes (if the Swift instance was created with the most-derived type) but not always. We might be able to get this back with some kind of global registry for the Java class -> Swift class mapping. - Subclassing a Java class from Swift doesn't work, but this makes it look like it could. We do hope to make this possible in the future.
- Class methods still don't get any of these benefits, although we might also be able to improve on this going forward.
Overall, I think mapping to Swift classes in the right thing to do, but it's not perfect. We'll need to work on sanding down the rough edges over time.
Thoughts?
Doug
Doug