文章目录

学习建议

目标层级 建议
初级 掌握基础层 8 题:@JvmStatic/@JvmOverloads、平台类型、companion/object 从 Java 怎么调;工程联想看追问第 1 条
中级 通读全文;进阶层能讲清 SAM 双向、@Throws、空安全注解互操作、顶层扩展的 Java 静态调用
高级 核心层加分项必答:集合可变性映射、out/in 通配符视角、suspend Continuation、R8 keep Kotlin metadata;能串联「注解→字节码→Java 调用面→混淆」链路

基础层(8 题)

#1 为什么 Android 混合工程必须理解 Kotlin-Java 互操作? 🔥

标准回答

现代 Android 工程几乎全是 Kotlin+Java 共存,互操作决定 API 能否被正确调用、空指针能否被编译期拦住。Kotlin 编译为 JVM 字节码后与 Java 共享同一套类型系统,但语法糖(默认参数、属性、companion)在 Java 侧有额外规则。

面试官可能继续追问

  • Android 混合工程里互操作常见场景?
    存量 Java 模块、三方 SDK、注解处理器生成代码、R8 混淆后反射;新 Kotlin API 未加 @Jvm* 注解会导致 Java 编译失败或 NPE。

  • 全 Kotlin 项目还需要学吗?
    需要,读库源码、接 Java SDK、写对外 API 给 Java 模块时仍会遇到。


#2 Kotlin 源码编译后在 JVM 上呈现什么? ⭐

标准回答

.kt 经 kotlinc 编译为 .class,与 Java 字节码同跑在 ART/Dalvik 上,类名、方法签名遵循 JVM 规范。顶层函数编译为 文件名Kt 的静态方法;object 生成单例类 + INSTANCEinline 在调用点展开,不单独生成可被 Java 直接调用的方法体。

面试官可能继续追问

  • Android 混合模块编译产物怎么合并?
    Gradle compileKotlincompileJava 产物进同一 dex;Java 只看到编译后的 .class 签名,看不到 Kotlin 语法糖。

  • 能看到 Kotlin 源码里的 val 吗?
    不能,Java 侧只见 getter 或 @JvmField 暴露的字段。


#3 什么是平台类型(platform type)?Java 调用 Kotlin 有什么风险? 🔥

标准回答

平台类型是 Kotlin 从 Java 继承/API 读入时,编译器无法确定可空性的类型,表现为 T!,可当作 TT? 使用。Java 无 Kotlin 空安全信息时,签名里的 String 在 Kotlin 侧是 String!;若 Java 实际返回 null,Kotlin 当非空用会 NPE。

面试官可能继续追问

  • Android 里平台类型怎么防 NPE?
    调用老旧 Java SDK、缺 @Nullable 的 Framework API 时主动 ?.;向 Java 暴露 API 应加 @Nullable/@NonNull 帮 Kotlin 侧消歧。

  • 平台类型会写进字节码吗?
    不会,只是 Kotlin 编译器前端概念;字节码仍是 Java 原始签名。


#4 Kotlin 属性与 Java 字段:getter/setter 还是 field? ⭐

标准回答

默认 val/var 编译为 private 字段 + getX()/setX(),Java 用 getter/setter 访问,不是直接字段访问。var name: StringgetName()/setName()val 只有 getter。@JvmField 才暴露 public 字段,跳过 accessor。

面试官可能继续追问

  • Android 里属性互操作注意什么?
    Data Binding、部分反射库、序列化框架若按字段名找属性,Kotlin 类需 @JvmField@get:JvmName 对齐;否则 Java 里 user.name 实际是方法调用。

  • is 前缀布尔属性 Java 怎么调?
    isActiveisActive(),不是 getIsActive()


#5 @JvmStatic 和 @JvmField 分别解决什么? 🔥

标准回答

@JvmStatic: companion / object 中的方法,生成真正静态方法,Java 可 Utils.doWork() 而非 Utils.INSTANCE.doWork()@JvmField:属性编译为 public 字段,Java 可 obj.count++,无 getter/setter。

面试官可能继续追问

  • Android 里 @JvmStatic/@JvmField 用在哪?
    companion object 里给 Java 用的工厂方法、常量应加 @JvmStatic/@JvmField;Firebase、Retrofit 等 Java 友好 API 常用此模式。

  • @JvmStatic 能用在顶层函数吗?
    顶层函数本身已是静态,不需要;主要用于 companion/object 成员。


#6 @JvmOverloads 如何让 Java 调用 Kotlin 默认参数? 🔥

标准回答

Kotlin 默认参数在 Java 不可见,Java 必须传齐所有参数;@JvmOverloads 为每个默认参数生成重载方法。编译器按「从右向左省略默认值」生成 foo()foo(a)foo(a,b) 等重载,Java 调用最近匹配签名。

面试官可能继续追问

  • Android 里 @JvmOverloads 什么时候加?
    给 Java 模块暴露的 Kotlin API(自定义 View 构造辅助、Builder)应在带默认值的构造/方法上加,否则 Java 无法省略参数。

  • 所有默认参数函数都该加吗?
    需要被 Java 调用的公开 API 加;纯 Kotlin 内部不必。


#7 companion object 从 Java 如何调用? ⭐

标准回答

companion 编译为带 Companion 内部类 + 外部类静态入口;Java 默认通过 OuterClass.Companion 访问。未标 @JvmStatic 的成员在 Java 里是 MyClass.Companion.doSomething();标了才是 MyClass.doSomething()

面试官可能继续追问

  • Android 里 companion 从 Java 怎么调?
    const val TAGconst val 已是编译期常量,Java 可直接 MyClass.TAG;普通 companion 方法需 @JvmStatic

  • companion 能实现 Java 接口吗?
    可以,但 Java 侧仍要通过 Companion 实例或 @JvmStatic 转静态。


#8 Kotlin object 单例在 Java 里怎么用? ⭐

标准回答

object Foo 编译为单例类,Java 通过 Foo.INSTANCE 访问唯一实例,再调实例方法。Kotlin 保证线程安全懒初始化;Java 无 object 语法,只看到 public static final Foo INSTANCE

面试官可能继续追问

  • Android 里 object 单例 Java 怎么调?
    object NetworkHolder 作全局持有很常见;给 Java 调用时文档要写清 XXX.INSTANCE.method(),或包一层 @JvmStatic 门面。

  • object 和 companion 单例区别?
    object 是独立单例类;companion 附属于外部类,命名空间不同。


进阶层(7 题)

#9 SAM 转换双向:Java 接口与 Kotlin lambda 如何互调? 🔥

标准回答

Java → Kotlin:Java SAM 接口在 Kotlin 调用处可写 lambda,如 view.setOnClickListener { }Kotlin → Java:Kotlin fun interface 或单抽象方法接口,Java 8+ 可用 lambda 实现;多抽象方法接口不行。

面试官可能继续追问

  • Android 里 SAM 互操作例子?
    RunnableOnClickListenerPredicate 等大量 SAM;Kotlin 写 lambda,Java 传 lambda 给 Kotlin 高阶函数时需函数式接口。

  • 接口有默认方法还算 SAM 吗?
    仅一个抽象方法仍可 SAM 转换。


#10 Kotlin 顶层/扩展函数在 Java 里如何静态调用? 💡

标准回答

顶层函数与扩展函数编译到 文件名Kt(或 @file:JvmName 指定名)的 static 方法;扩展函数第一个参数是接收者。fun String.lastSegment()StringExtKt.lastSegment(str);Java 无扩展语法,必须显式传接收者。

面试官可能继续追问

  • Android 里扩展函数 Java 怎么调?
    Context.ktfun Context.dp() 在 Java 为 ContextExtKt.dp(context, 16);对 Java 暴露扩展 API 时用 @file:JvmName("ContextUtils") 稳定类名。

  • 扩展能在 Java 里像实例方法吗?
    不能,永远是静态 + 接收者参数。


#11 @Throws 如何处理 Kotlin 与 Java 受检异常差异? ⭐

标准回答

Kotlin 无受检异常,但 Java 调用 Kotlin 抛出的异常时,编译器不把 Kotlin 异常当 checked;@Throws 让 Kotlin 函数在 Java 签名上 声明 异常,逼 Java 处理或 throws。@Throws(IOException::class) 写入字节码 throws IOException;多异常可列多个 class。

面试官可能继续追问

  • Android 里 @Throws 什么时候加?
    封装 IO、解析逻辑给 Java 模块用时,若希望 Java 方 try-catch,应加 @Throws;纯 Kotlin 调用方仍按运行时异常处理。

  • Kotlin 调 Java 受检异常呢?
    Kotlin 不强制 catch,但异常仍会抛出,建议显式处理。


#12 空安全注解互操作:@Nullable、@NonNull 与平台类型 ⭐

标准回答

Kotlin 读 Java 时,JSR-305 / JetBrains @Nullable/@NonNull / Android @RecentlyNullable 等会影响推断为 T?T;缺失则变平台类型。@Nonnull 参数在 Kotlin 传 null 可编译警告;Kotlin 向 Java 暴露 API 应用 @Nullable/@NotNull 标注,避免 Java 误用。

面试官可能继续追问

  • Android 里空安全注解互操作注意什么?
    androidx 与 JetBrains 注解在 AGP 默认开启增强;对接 Gson/Retrofit 模型时,Java 字段注解决定 Kotlin 可空性,混用不当是混合工程 NPE 主因之一。

  • @NonNull@NotNull 一样吗?
    工具链大多等价识别,项目应统一一套。


#13 internal 可见性能跨模块吗? 💡

标准回答

internal同一 Gradle module 内可见,编译为 public 字节码(名称可能 mangling),其他 module 的 Java/Kotlin 不能直接访问源码级 internal API。跨模块靠模块边界隔离,不是 Java package-private@PublishedApi internal 可特例暴露给公开 inline API。

面试官可能继续追问

  • Android 多 module 里 internal 怎么用?
    Kotlin internal 实现细节不会泄漏给 :feature 以外的模块;但反射或同一 dex 内仍可能看到类名,勿放敏感逻辑。

  • Java 能调 internal 吗?
    同模块 Java 可以(编译后 public),异模块不行。


#14 @file:JvmName / @JvmName 解决哪些命名冲突? ⭐

标准回答

@file:JvmName(“FooUtil”):改顶层函数/属性生成的 Java 类名,默认 FileNameKtFooUtil@JvmName(“bar”):改单个函数/属性的 JVM 签名名,解决擦除后签名冲突或 Java 关键字问题。

面试官可能继续追问

  • Android 里 JvmName 典型场景?
    utils.kt 一堆扩展时,Java 侧用稳定 DisplayUtils.dp()DisplayUtilsKt.dp() 更友好;与 Java 已有类名冲突时必须重命名。

  • 改 JvmName 会影响反射吗?
    会,反射要用 JVM 上的实际名。


#15 inline 函数对 Java 可见吗?reified 呢? 💡

标准回答

inline 函数在 Java 不能像 Kotlin 那样内联调用;若未 noinline 的参数,Java 甚至无法直接调用该 inline 函数(需非 inline 包装或拆非 inline API)。reified 依赖内联在调用点展开具体类型,Java 完全不可用

面试官可能继续追问

  • Android 里 inline/reified 给 Java 暴露怎么办?
    提供非 inline 重载或 Class<T> 入参版本;inline reified 仅 Kotlin 内部使用,与 TypeToken 思路类似。

  • 性能敏感 API 给 Java 怎么办?
    提供普通函数 + 文档说明 Kotlin 侧用 inline 版本。


核心层(7 题)

#16 Kotlin 与 Java 集合类型如何映射?可变性陷阱在哪? 🔥

标准回答

List<T>(Kotlin)→ List<T>(Java 只读视图);MutableList<T>java.util.List(可写)。Kotlin 只读 List 可能是 Java 可变 List 的包装,只读≠不可变kotlin.collections 很多是 Java 集合的别名/包装;Arrays.asList 返回固定大小列表,Kotlin 当 List 用仍可能 UnsupportedOperationException

面试官可能继续追问

  • Android API 边界集合怎么防踩坑?
    Kotlin 模块返回 List<T> 给 Java,若内部是 mutableListOf 且引用外泄,Java 仍可 add 破坏不变性;对外应 toList() 防御性拷贝。

  • Set/Map 同理吗?
    是,MutableMap 对应可写 java.util.Map


#17 从 Java 视角看 Kotlin out/in 与通配符? 🔥

标准回答

Kotlin out T(协变)编译为 Java ? extends Tin T(逆变)→ ? super T;声明处型变在 Java 签名上就是通配符。Java 调用 Kotlin List<out String> 只能读 String,不能 add String;Kotlin 调用 Java List<? extends Foo> 时类型是 List<out Foo> 或平台类型。

面试官可能继续追问

  • Android 里 out/in 互操作怎么用?
    Repository 返回 List<out UiModel> 给 Java 展示层安全;若 Java 侧需要 add,应用 MutableList 或 Kotlin in 消费者类型,与 PECS 原则一致。

  • 星投影 List<*> Java 签名?
    类似 List<?> 或原始 List,读作未知元素。


#18 @JvmWildcard 和 @JvmSuppressWildcards 有何用? 💡

标准回答

@JvmWildcard:强制 Kotlin 类型参数在 Java 签名带通配符,如 List<String>List<? extends String>,避免 Java RAW 赋值破坏协变。@JvmSuppressWildcards:抑制通配符,生成 Java 友好的具体类型签名,便于 Java 调用方少写 ? extends

面试官可能继续追问

  • Android 里泛型 API 给 Java 暴露怎么调?
    暴露给 Java 的泛型 API(缓存 Map<K,V>、回调 List<T>)若 Java 编译报错或警告过多,用二者微调签名;Kotlin 调用方仍按原泛型规则检查。

  • 能标在类上还是函数上?
    可用于类、函数、类型参数位置,粒度不同。


#19 data class 从 Java 侧如何构造、读取与 copy? ⭐

标准回答

data class 仍生成 getter(getName())、componentN()copy 实例方法、equals/hashCode/toString Java 可用的全参构造若属性有默认值且未 @JvmOverloads。Java new User(id, name) 需主构造所有参数在 Java 可见;copy 在 Java 要按参数顺序调用。

面试官可能继续追问

  • Android 里 data class 给 Java 用注意什么?
    网络/DTO 模型给 Java 用时应 @JvmOverloads 构造、或提供 Builder;Java 用 getter 即可,少用 component1()

  • data class 能继承 Java 类吗?
    可以继承类,但不能同时继承另一个 Kotlin 类(除 Any)。


#20 Java 如何调用 Kotlin suspend?Continuation 互操作要点 🔥

标准回答

suspend 函数编译为带 Continuation<? super T> 末参 的普通方法;Java 调用需传入 Continuation 实现,在 resumeWith 里拿结果或异常。kotlinc 状态机 + Continuation 回调;Java 不享受协程语法糖,应优先用库提供的 Callback 包装 或让 Kotlin 侧提供非 suspend 入口。

面试官可能继续追问

  • Android 里 Java 怎么调 Kotlin suspend?
    Room、Retrofit suspend API 给 Java 用时常在 Kotlin 层包 Callback/ListenableFuture;Java 直接调 suspend 仅适合框架集成,业务代码避免手写 Continuation。

  • Continuation 最后一个参数 Java 要手写吗?
    是,且需正确 CoroutineContext,工程上极少手写。


#21 混合模块、AGP 与 R8 如何保留 Kotlin metadata? 🔥

标准回答

R8 混淆会剥离 Kotlin Metadata@kotlin.Metadata),导致反射、部分序列化、Moshi/Gson Kotlin 适配器、ViewBinding 泛型推断失效;需 -keep class kotlin.Metadata { *; }@Keep 相关规则。Kotlin 可见性、默认参数、内联、属性名依赖 metadata;纯 Java 反射只看 .class 够用,混合库不够。

面试官可能继续追问

  • Android release 混淆 Kotlin 类挂了怎么办?
    AGP 8 + R8 full mode 默认开启;release 崩溃若涉及反射/Parcelize,先查 proguard-rules 是否 keep 模型;库作者应发布 consumer-proguard-rules.pro

  • 多 module 只 keep app 够吗?
    各 library 应自带 consumer rules,避免 feature module 被错剥。


#22 大型 Kotlin+Java 混合项目互操作最佳实践? ⭐

标准回答

边界清晰:新代码 Kotlin 优先;Java 存量包门面化,对外 API 用 @JvmStatic/@JvmOverloads/@Nullable 标注。API 设计:给 Java 的 public API 避免 inline/reified、suspend 直出、internal 泄漏;集合返回防御性只读拷贝。构建与混淆:统一注解、各 module 发布 consumer ProGuard。

面试官可能继续追问

  • Android 大型混合项目怎么分层?
    :core Kotlin 实现 + :api Java 接口模块、Retrofit 接口放 Java、实现放 Kotlin 是常见分层;团队规范写清「谁调谁、哪些注解必选」。

  • Compose 项目还有 Java 互操作吗?
    有,View 互操作、AndroidView、存量 Java Activity 调 Kotlin Composable 桥接。
    在这里插入图片描述


面试策略速查

面试等级 建议掌握
初级 ★★★☆☆
中级 ★★★★☆
高级 ★★★★★

必背知识(🔥)

平台类型、@JvmStatic/@JvmField/@JvmOverloads、Java 调 Kotlin 默认参数、SAM 双向、集合可变性映射、out/in 与 Java 通配符、suspend Continuation 桥接、R8 Kotlin metadata

高频追问

「Java 怎么调 companion」「默认参数为什么编译不过」「平台类型 NPE 怎么防」「MutableList 和 List 边界」「release 混淆后 Kotlin 反射挂了」

加分项(💡)

顶层扩展 Java 静态调用、internal 模块边界、inline/reified Java 不可用、@JvmWildcard/@JvmSuppressWildcards

容易踩坑

Kotlin API 未加 @JvmOverloads 导致 Java 编译失败;把 Kotlin List 当不可变;Java 调 suspend 手写 Continuation;R8 未 keep metadata;@JvmField 滥用破坏封装


完整链路一句通

互操作一条线:Kotlin 编译为 JVM 字节码后与 Java 共享类型系统,语法糖靠 @Jvm* 与注解暴露正确调用面;平台类型与集合可变性是 NPE 主因;suspend 以 Continuation 末参桥接、业务应 Kotlin 包装;R8 剥离 metadata 需 consumer rules,混合工程用模块边界 + 注解统一 + API 门面化保长期可维护。


相关推荐

Android 高级工程师面试:Kotlin 语法基础 近1年高频追问 22 题

Android 高级工程师面试:Java 基础知识 近1年高频追问 22 题

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐