Skip to content

Desktop (JVM) 互操作

源:Kotlin for JVM

Kotlin/JVM 是 Kotlin Multiplatform 项目中与 Java 生态集成最紧密的平台。理解如何利用 JVM 平台的强大功能,对于构建桌面应用或服务端组件至关重要。

Kotlin 调用 Java API

kotlin
// jvmMain
import java.io.File
import java.util.concurrent.Executors
import javax.swing.JFrame
import javax.swing.JButton

// 调用 Java File API
fun readFile(path: String): String {
    return File(path).readText()
}

fun writeFile(path: String, content: String) {
    File(path).writeText(content)
}

// 使用 Java 集合
fun processJavaList(javaList: java.util.List<String>): List<String> {
    return javaList.map { it.uppercase() }
}

// 使用 ExecutorService
fun runAsync(task: () -> Unit) {
    val executor = Executors.newSingleThreadExecutor()
    executor.submit(task)
    executor.shutdown()
}

// Swing GUI
fun createWindow() {
    val frame = JFrame("Kotlin Desktop App")
    val button = JButton("Click Me").apply {
        addActionListener {
            println("Button clicked!")
        }
    }
    frame.add(button)
    frame.setSize(300, 200)
    frame.isVisible = true
}

Java 类型映射

KotlinJava
kotlin.Stringjava.lang.String
kotlin.Intint / java.lang.Integer
kotlin.collections.Listjava.util.List
kotlin.ArrayJava 数组 T[]
Unitvoid

Java 调用 Kotlin 代码

@JvmStatic 注解

kotlin
// Kotlin
object Utils {
    @JvmStatic
    fun format(text: String): String {
        return text.uppercase()
    }
}

class Config {
    companion object {
        @JvmStatic
        val DEFAULT_TIMEOUT = 30
        
        @JvmStatic
        fun create(): Config = Config()
    }
}
java
// Java 调用
String formatted = Utils.format("hello"); // 静态调用

int timeout = Config.DEFAULT_TIMEOUT;
Config config = Config.create();

@JvmField 注解

kotlin
// Kotlin
class Server {
    @JvmField
    var port: Int = 8080 // 暴露为 public field
    
    var host: String = "localhost" // 生成 get/set 方法
}
java
// Java 调用
Server server = new Server();
server.port = 9000; // 直接访问字段
server.setHost("0.0.0.0"); // 通过 setter

@JvmOverloads 注解

kotlin
// Kotlin - 生成多个重载方法
class User @JvmOverloads constructor(
    val name: String,
    val age: Int = 18,
    val email: String = ""
)
java
// Java 可以使用
User user1 = new User("Alice");
User user2 = new User("Bob", 25);
User user3 = new User("Charlie", 30, "charlie@example.com");

Compose Desktop 集成

标准代码块

kotlin
// desktopMain
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application

@Composable
@Preview
fun App(viewModel: UserViewModel) {
    var users by remember { mutableStateOf(emptyList<User>()) }
    
    LaunchedEffect(Unit) {
        users = viewModel.loadUsers()
    }
    
    MaterialTheme {
        Column(modifier = Modifier.padding(16.dp)) {
            Text("Users", style = MaterialTheme.typography.headlineMedium)
            
            Spacer(modifier = Modifier.height(8.dp))
            
            users.forEach { user ->
                Card(modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)) {
                    Text(user.name, modifier = Modifier.padding(16.dp))
                }
            }
        }
    }
}

fun main() = application {
    Window(onCloseRequest = ::exitApplication, title = "My App") {
        val viewModel = UserViewModel()
        App(viewModel)
    }
}

平台特定功能

kotlin
// desktopMain
import java.awt.Desktop
import java.net.URI

// 打开浏览器
fun openBrowser(url: String) {
    if (Desktop.isDesktopSupported()) {
        Desktop.getDesktop().browse(URI(url))
    }
}

// 系统托盘
import java.awt.SystemTray
import java.awt.TrayIcon
import java.awt.Toolkit

fun createTrayIcon() {
    if (SystemTray.isSupported()) {
        val tray = SystemTray.getSystemTray()
        val image = Toolkit.getDefaultToolkit().getImage("icon.png")
        val trayIcon = TrayIcon(image, "My App")
        tray.add(trayIcon)
    }
}

文件系统访问

标准代码块

kotlin
// jvmMain
import java.io.File
import java.nio.file.*
import kotlin.io.path.*

// 读写文件
fun saveData(filename: String, data: String) {
    File(filename).writeText(data)
}

fun loadData(filename: String): String {
    return File(filename).readText()
}

// 使用 Path API
fun listFiles(directory: String): List<String> {
    return Path(directory).listDirectoryEntries()
        .map { it.name }
}

// 监听文件变化
fun watchDirectory(path: String, onChange: (String) -> Unit) {
    val watchService = FileSystems.getDefault().newWatchService()
    val dir = Paths.get(path)
    dir.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY)
    
    while (true) {
        val key = watchService.take()
        for (event in key.pollEvents()) {
            onChange(event.context().toString())
        }
        key.reset()
    }
}

// 应用数据目录
fun getAppDataDir(): String {
    val os = System.getProperty("os.name").lowercase()
    return when {
        os.contains("win") -> System.getenv("APPDATA")
        os.contains("mac") -> "${System.getProperty("user.home")}/Library/Application Support"
        else -> "${System.getProperty("user.home")}/.config"
    } + "/MyApp"
}

多线程与并发

标准代码块

kotlin
// jvmMain
import java.util.concurrent.*
import kotlinx.coroutines.*

// ExecutorService
class TaskExecutor {
    private val executor = Executors.newFixedThreadPool(4)
    
    fun execute(task: () -> Unit) {
        executor.submit(task)
    }
    
    fun shutdown() {
        executor.shutdown()
        executor.awaitTermination(10, TimeUnit.SECONDS)
    }
}

// CompletableFuture 与协程互操作
suspend fun <T> CompletableFuture<T>.await(): T {
    return suspendCancellableCoroutine { cont ->
        whenComplete { result, exception ->
            if (exception != null) {
                cont.resumeWithException(exception)
            } else {
                cont.resume(result)
            }
        }
    }
}

// 使用示例
suspend fun fetchData(): String {
    val future = CompletableFuture.supplyAsync {
        // 耗时操作
        "Data"
    }
    return future.await()
}

JDBC 数据库访问

标准代码块

kotlin
// jvmMain
import java.sql.*

class DatabaseManager(private val url: String) {
    private var connection: Connection? = null
    
    fun connect() {
        connection = DriverManager.getConnection(url)
    }
    
    fun query(sql: String): List<Map<String, Any?>> {
        val statement = connection?.createStatement() ?: return emptyList()
        val resultSet = statement.executeQuery(sql)
        val metadata = resultSet.metaData
        val columnCount = metadata.columnCount
        
        val results = mutableListOf<Map<String, Any?>>()
        while (resultSet.next()) {
            val row = mutableMapOf<String, Any?>()
            for (i in 1..columnCount) {
                row[metadata.getColumnName(i)] = resultSet.getObject(i)
            }
            results.add(row)
        }
        return results
    }
    
    fun execute(sql: String): Int {
        val statement = connection?.createStatement() ?: return 0
        return statement.executeUpdate(sql)
    }
    
    fun close() {
        connection?.close()
    }
}

// 使用示例
fun getUsers(): List<User> {
    val db = DatabaseManager("jdbc:sqlite:app.db")
    db.connect()
    
    val rows = db.query("SELECT * FROM users")
    val users = rows.map { row ->
        User(
            id = row["id"] as Long,
            name = row["name"] as String
        )
    }
    
    db.close()
    return users
}

系统集成

系统属性与环境变量

kotlin
// jvmMain
fun getSystemInfo(): Map<String, String> {
    return mapOf(
        "os" to System.getProperty("os.name"),
        "osVersion" to System.getProperty("os.version"),
        "javaVersion" to System.getProperty("java.version"),
        "userHome" to System.getProperty("user.home"),
        "tempDir" to System.getProperty("java.io.tmpdir")
    )
}

fun getEnv(name: String): String? {
    return System.getenv(name)
}

进程管理

kotlin
// jvmMain
fun runCommand(command: String): String {
    val process = Runtime.getRuntime().exec(command)
    val output = process.inputStream.bufferedReader().readText()
    process.waitFor()
    return output
}

// 使用 ProcessBuilder
fun runCommandWithArgs(vararg args: String): String {
    val process = ProcessBuilder(*args)
        .redirectErrorStream(true)
        .start()
    
    val output = process.inputStream.bufferedReader().readText()
    process.waitFor()
    return output
}

Gradle 配置

JVM 目标配置

kotlin
// shared/build.gradle.kts
kotlin {
    jvm {
        compilations.all {
            kotlinOptions {
                jvmTarget = "17"
                freeCompilerArgs += listOf(
                    "-Xjsr305=strict",
                    "-Xopt-in=kotlin.RequiresOptIn"
                )
            }
        }
        
        testRuns.all {
            executionTask.configure {
                useJUnitPlatform()
            }
        }
    }
    
    sourceSets {
        val jvmMain by getting {
            dependencies {
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.9.0")
                implementation("org.xerial:sqlite-jdbc:3.47.1.0")
            }
        }
        
        val jvmTest by getting {
            dependencies {
                implementation(kotlin("test-junit5"))
                implementation("org.junit.jupiter:junit-jupiter:5.11.4")
            }
        }
    }
}

Desktop 应用打包

kotlin
// build.gradle.kts
import org.jetbrains.compose.desktop.application.dsl.TargetFormat

compose.desktop {
    application {
        mainClass = "com.example.MainKt"
        
        nativeDistributions {
            targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
            packageName = "My App"
            packageVersion = "1.0.0"
            
            macOS {
                iconFile.set(project.file("icon.icns"))
            }
            windows {
                iconFile.set(project.file("icon.ico"))
            }
            linux {
                iconFile.set(project.file("icon.png"))
            }
        }
    }
}

最佳实践

✅ 实践 1:使用 Kotlin 扩展优化 Java API

kotlin
// ✅ 创建扩展函数
fun File.ensureExists(): File {
    if (!exists()) {
        parentFile?.mkdirs()
        createNewFile()
    }
    return this
}

// 使用
File("data/app.db").ensureExists().writeText("data")

✅ 实践 2:资源管理 use 函数

kotlin
// ✅ 自动关闭资源
fun readFile(path: String): String {
    return File(path).inputStream().use { stream ->
        stream.bufferedReader().readText()
    }
}

// ❌ 手动关闭
val stream = File(path).inputStream()
try {
    return stream.bufferedReader().readText()
} finally {
    stream.close()
}

✅ 实践 3:协程替代回调

kotlin
// ✅ 使用协程
suspend fun loadData(): Data = withContext(Dispatchers.IO) {
    // 耗时操作
}

// ❌ 回调地狱
fun loadData(callback: (Data) -> Unit) {
    Thread {
        val data = /* ... */
        callback(data)
    }.start()
}

✅ 实践 4:平台检测

kotlin
enum class Platform {
    WINDOWS, MACOS, LINUX, UNKNOWN
}

fun currentPlatform(): Platform {
    val os = System.getProperty("os.name").lowercase()
    return when {
        os.contains("win") -> Platform.WINDOWS
        os.contains("mac") -> Platform.MACOS
        os.contains("nux") -> Platform.LINUX
        else -> Platform.UNKNOWN
    }
}

Kotlin/JVM 提供了与 Java 生态的完美互操作,使得开发者可以利用庞大的 Java 库生态,同时享受 Kotlin 语言的现代特性。