公司动态

Android随笔-Kotlin和Java如何相互兼容

📅 2026/7/15 22:23:27
Android随笔-Kotlin和Java如何相互兼容
Kotlin 兼容 Java 不是勉强能跑而是设计之初的核心目标——JetBrains当年推出 KotlinGoogle是采纳和推广 就是为了在庞大的 Java 生态中无缝替换而不是另起炉灶。一、源码级兼容能混写、能互调1. 同一个项目里 Kotlin 和 Java 共存src/ ├── main/ │ ├── java/ │ │ └── com/example/ │ │ └── JavaUtils.java ← Java 文件 │ └── kotlin/ │ └── com/example/ │ └── KotlinService.kt ← Kotlin 文件Gradle 编译时Kotlin 编译器kotlinc和 Java 编译器javac会协同工作最终生成统一的 JVM 字节码。2. 互相调用零成本Kotlin 调用 Java// 直接调用 Java 类没有任何桥接层vallistArrayListString()// java.util.ArrayListlist.add(hello)valsizelist.size// 直接访问 Java 的 getterJava 调用 Kotlin// Kotlin 文件StringUtils.ktpackagecom.examplefunformatName(name:String):Stringname.trim().uppercase()classUser(valname:String,varage:Int)// Java 文件直接调用StringformattedStringUtilsKt.formatName( alice );// 自动生成 StringUtilsKt 类UserusernewUser(Bob,25);Stringnameuser.getName();// Kotlin 的 val 属性生成 Java getteruser.setAge(26);// Kotlin 的 var 属性生成 Java getter setter二、编译层兼容同一份字节码Kotlin 编译器会把 Kotlin 代码编译成与 Java 编译器相同的 JVM 字节码.class文件这意味着JVM 分不清这段字节码是 Kotlin 写的还是 Java 写的所有 Java 工具Profiler、反编译器、字节码插桩都能直接用反射、代理、AOP 等机制完全通用反编译示例// KotlindataclassUser(valname:String,varage:Int)编译后反编译成 Java 大致是publicfinalclassUser{NotNullprivatefinalStringname;privateintage;publicUser(NotNullStringname,intage){...}NotNullpublicfinalStringgetName(){returnthis.name;}publicfinalintgetAge(){returnthis.age;}publicfinalvoidsetAge(intage){this.ageage;}// 自动生成 equals、hashCode、toString、copy、componentN}三、语言特性映射Kotlin 的语法糖在字节码层面还原Kotlin 很多高级特性最终都会脱糖成 Java 能理解的字节码Kotlin 特性编译后的字节码表现val/var属性生成private final字段 getter/setter数据类data class生成equals、hashCode、toString、copy、componentN扩展函数编译为静态方法第一个参数是接收者对象顶层函数生成包含该函数的*Kt类如StringUtilsKt默认参数生成重载方法无参版本调用全参版本传默认值伴生对象companion object生成静态内部类 单例实例Lambda生成FunctionN接口的实现类或内联优化协程suspendCPS 变换生成状态机代码空安全?/!!插入空检查抛出NullPointerException默认参数示例// Kotlinfungreet(name:String,greeting:StringHello){println($greeting,$name!)}编译后生成两个 Java 方法publicstaticvoidgreet(NotNullStringname,NotNullStringgreeting){...}// 重载方法供 Java 调用publicstaticvoidgreet(NotNullStringname){greet(name,Hello);// 传入默认值}Java 调用时可以直接StringUtilsKt.greet(Alice)不需要传第二个参数。四、Java 没有的特性Kotlin 如何桥接1. 空安全Kotlin 的空安全在 Java 中无法直接表达所以编译器会插入Nullable/NotNull注解funprocess(text:String?){}// text 可为 null编译后publicstaticvoidprocess(NullableStringtext){}Java 调用时IDE 会根据注解提示可能空指针。2. 扩展函数funString.addExclamation():Stringthis!编译后publicstaticfinalStringaddExclamation(NotNullString$this){return$this!;}Java 调用StringresultStringUtilsKt.addExclamation(hello);3. 命名参数与默认参数Java 不支持命名参数Kotlin 编译器生成JvmOverloads注解来生成重载JvmOverloadsfuncreateUser(name:String,age:Int0,email:String){}Java 可以调用createUser(Alice)createUser(Alice, 25)createUser(Alice, 25, ab.com)五、Java 调用 Kotlin 的注意事项1. 平台类型Platform TypesJava 代码没有空安全注解时Kotlin 将其视为平台类型String!可空可非空// Java: public String getName() { return name; }// Kotlin 中返回类型是 String!平台类型valname:StringjavaObj.getName()// 可能运行时 NPEvalnameOrNull:String?javaObj.getName()// 安全2.internal可见性Kotlin 的internal模块内可见在 Java 中会被编译为public因为 JVM 没有internal概念所以 Java 代码可以访问Kotlin 的internal成员——这是编译层面的一个漏洞。3. 顶层属性// KotlinconstvalMAX_COUNT100Java 调用intmaxConstantsKt.MAX_COUNT;// 生成静态常量六、Kotlin 独有的运行时标准库兼容Kotlin 标准库kotlin-stdlib大量复用 Java 标准库Kotlin 类型底层实现kotlin.String就是java.lang.Stringkotlin.collections.List就是java.util.Listkotlin.collections.ArrayList就是java.util.ArrayListkotlin.Int编译为int基本类型或Integer装箱kotlin.Any就是java.lang.Object所以 Kotlin 的集合操作vallistlistOf(1,2,3)// 返回 java.util.Listlist.filter{it1}// 返回新的 java.util.List底层都是 Java 集合只是 Kotlin 通过扩展函数给它们加了新能力。七、总结兼容的层次┌─────────────────────────────────────────┐ │ 源码层Kotlin 和 Java 文件可混写、互调 │ ├─────────────────────────────────────────┤ │ 编译层都编译为相同格式的 JVM 字节码 │ ├─────────────────────────────────────────┤ │ 字节码层JVM 不区分来源反射/AOP 通用 │ ├─────────────────────────────────────────┤ │ 运行时层共用 Java 标准库、JVM 内存模型 │ ├─────────────────────────────────────────┤ │ 生态层Maven/Gradle 依赖完全互通 │ └─────────────────────────────────────────┘Kotlin 的兼容策略是渐进式替换现有 Java 项目可以逐步引入 Kotlin不需要重写团队可以一部分人用 Java、一部分用 Kotlin互不干扰最终可以 100% Kotlin但底层依然是 JVM 生态这也是为什么 Google 敢把它定为 Android 首选语言——它不是在推翻 Java而是在 Java 的地基上盖了栋新楼。