Explore Swift and Java interoperability
Swift 进阶 2m

探索 Swift 与 Java 互操作

Explore Swift and Java interoperability

2025年6月9日

在 Apple 官方观看视频

一句话判断

Swift 要走出 Apple 生态了——SwiftJava 让 Swift 和 Java 在同一个项目里共存,双向互调,用 Gradle/SwiftPM 集成构建,而且是开源的。这对服务端 Swift 和企业级迁移意义重大。

这场 Session 讲了什么

Session 介绍了 Swift-Java 互操作项目(SwiftJava),覆盖三个场景:在 Java 应用中用 Swift 实现单个 native 方法(通过 JNI + SwiftJava 工具链简化)、从 Swift 中调用整个 Java 库(通过 Gradle 依赖解析 + JavaKit 生成桥接代码)、以及把整个 Swift 库暴露给 Java 使用(通过 SwiftKit + Foreign Function and Memory API)。

SwiftJava 的定位和 C++ 互操作类似——不是简单的 FFI 绑定,而是深度集成两种语言的运行时特性:类继承、泛型、内存管理(ARC vs GC)、错误处理(Swift Error vs Java Exception)。项目完全开源,托管在 Swiftlang GitHub 组织下。

值得深挖的点

JNI 的痛苦 vs SwiftJava 的优雅

Session 先展示了一段”裸 JNI”代码——用 Java 的 native method 特征生成 C 头文件,然后手动实现 JNI 函数。光是方法签名、JNI 环境指针、对象生命周期管理这些样板代码就让人头大,而且极易出错导致 fatal crash。

SwiftJava 的做法:用 swift-java 命令行工具解析 Java 类,生成 Swift 端的类型装饰(包含方法签名、属性等),以及一个 NativeMethods protocol。开发者只需要写 extension 遵循这个 protocol,加上 @JavaImplementation@JavaMethod 宏即可。对象生命周期由 JavaKit 自动管理(提升为 global reference),不需要手动 retain/release。

从 Swift 调用 Java 库:Gradle 集成

SwiftJava 能自动解析 Java 库及其传递依赖。只需要提供 Maven 坐标(GroupId:ArtifactId:Version),工具会通过 Gradle 解析所有依赖,生成 Swift 端的类型包装。

两种集成方式:SwiftPM build plugin(自动在构建时触发 Gradle 解析,但需要关闭 SwiftPM 的安全沙箱)或 swift-java resolve 命令行工具(手动触发,不碰沙箱限制)。生成的 Swift 代码可以直接 import,用法和原生 Swift 类型一样——包括用 for-in 循环遍历 Java 集合。

从 Java 调用 Swift 库:Foreign Function API

这是最令人兴奋的方向——让 Swift 在 Java 项目中”一等公民”。SwiftJava 用 Java 22 稳定化的 Foreign Function and Memory API(FFM),而不是 JNI。FFM 提供了更好的原生内存控制和调用效率。

关键设计是 SwiftArena——管理 Swift 对象在原生堆上的生命周期。Java wrapper 对象在 JVM 堆上由 GC 管理,wrapper 被回收时触发原生 Swift 值的销毁。但更好的做法是用 Confined Arena + Java 的 try-with-resources 语法,确保 Swift 对象在确定时机销毁,不给 GC 增加负担。

Swift struct 是值类型,没有稳定的对象标识——SwiftJava 的处理是把它分配到原生堆上(而非栈),从 Java wrapper 通过 memory segment 指向它。

内存管理的跨语言协调

这是互操作中最微妙的部分。Swift 用 ARC,Java 用 GC。SwiftJava 在两个方向都做了处理:

  • Swift 调 Java:JavaKit 把 Java 对象引用提升为 JNI global reference,避免被 GC 回收的同时 Swift 还在用。
  • Java 调 Swift:SwiftArena 管理原生内存,try-with-resources 确保确定性销毁。

Session 强烈建议用 Confined Arena(作用域销毁)而非 Auto Arena(依赖 GC finalize),因为 finalization 会给 GC 带来大量额外追踪负担,且销毁时机不可控。

代码片段

1. Swift 实现 Java Native Method

场景:在 Java 应用中用 Swift 实现一个计算函数。

// Java 端声明
// public class JNIExample {
//     public native Integer compute(Integer input);
// }

// Swift 端实现
import JavaKit

extension JNIExample: JNIExampleNativeMethods {
    @JavaMethod
    func compute(_ input: JavaLangInteger?) -> JavaLangInteger? {
        // 可以自由使用任何 Swift 库
        import CryptoKit
        let hash = SHA256.hash(data: Data(input!.intValue().bigEndian.bytes))
        return JavaLangInteger(Int32(hash.first ?? 0))
    }
}

2. Java 调用 Swift 库

场景:在 Java 应用中使用 Swift 实现的业务逻辑。

// Swift 端定义了 SwiftyBusiness struct
// SwiftJava 生成了对应的 Java 类
import com.example.swiftlib.*;

try (SwiftArena arena = SwiftArena.ofConfined()) {
    // 像普通 Java 类一样使用 Swift 类型
    SwiftyBusiness biz = new SwiftyBusiness("Coffee Shop", 42, arena);
    System.out.println(biz.name());  // "Coffee Shop"
    biz.updateValue(100);
    System.out.println(biz.value()); // 100
} // arena 关闭时,Swift 对象被确定性销毁

坑:用 try-with-resources + Confined Arena 管理生命周期,不要依赖 GC finalize——性能差且时机不可控。

最佳实践

如果你的团队有 Java 代码库想逐步引入 Swift,从”单个 native method”开始——风险最低,能立刻验证 SwiftJava 工具链在你的项目里能否正常工作。

构建集成方面,如果能接受关闭 SwiftPM 沙箱,用 build plugin 最方便(自动解析依赖)。否则用命令行工具手动 resolve,虽然多一步但更安全。

内存管理上,Java 调 Swift 时一律用 Confined Arena + try-with-resources。这是 Session 明确推荐的模式,性能和正确性都优于依赖 GC。

项目是开源的,处于早期阶段——如果你遇到 bug 或有功能需求,直接去 Swiftlang GitHub 提 issue 或 PR。社区参与对这类基础设施项目特别重要。

还有什么值得关注

  • C++/Swift/C 混合互操作(Session 提到):和 Java 互操作互补,C/C++ 互操作已经稳定两年,今年有” Safely Mixed C, C++ and Swift”的新 Session。
  • Gradle 集成的进展:Session 提到这是”进行中的工作”,未来可能有更深度的 Gradle plugin 支持。
  • Swift on Server 生态:SwiftJava 的出现让 Swift 在服务端有了更多落地场景——你的 Java 后端可以渐进式引入 Swift 模块,不需要全面重写。
Swift