Kotlin 语言互操作性与可空性详解
一、Kotlin 与 Java 互操作性
1. 基础互操作
从 Kotlin 调用 Java 代码
fun main() {
val javaObj = JavaClass("Kotlin")
println(javaObj.name)
javaObj.name = "Java"
println(javaObj.name)
}
类型映射
| Java 类型 | Kotlin 类型 |
|---|
byte | Byte |
short | Short |
int | Int |
long | Long |
float | Float |
double | Double |
char | Char |
boolean | Boolean |
java.lang.Integer | Int? |
java.lang.String | String? |
java.lang.Object | Any |
void | Unit |
2. 空安全与平台类型
平台类型(Platform Types)
fun processJavaObject(obj: JavaClass) {
val name = obj.name
val length1: Int? = name?.length
val length2: Int = name!!.length
val length3: Int = name?.length ?: 0
val safeName: String? = obj.name
val unsafeName: String = obj.name
}
使用注解改进互操作
fun useAnnotatedClass(obj: AnnotatedJavaClass) {
val nonNull: String = obj.nonNullName
val nullable: String? = obj.nullableName
}
3. SAM 转换(函数式接口)
val listener = OnClickListener { view ->
println("View clicked: ${view.id}")
}
val callback = Callback<String> { result ->
println("Result: $result")
}
4. 集合互操作
fun processJavaList(javaList: List<String>) {
val kotlinList: List<String> = javaList
val mutableList = javaList.toMutableList()
mutableList.add("new item")
val javaMutable: MutableList<String> = getMutableStrings()
javaMutable.add("another item")
}
集合类型映射
| Java 类型 | Kotlin 只读类型 | Kotlin 可变类型 |
|---|
List<T> | List<T> | MutableList<T> |
Set<T> | Set<T> | MutableSet<T> |
Map<K, V> | Map<K, V> | MutableMap<K, V> |
ArrayList<T> | MutableList<T> | MutableList<T> |
HashSet<T> | MutableSet<T> | MutableSet<T> |
HashMap<K, V> | MutableMap<K, V> | MutableMap<K, V> |
5. 异常处理互操作
fun callRiskyJavaMethod() {
try {
riskyMethod()
} catch (e: IOException) {
println("捕获到IO异常: ${e.message}")
}
}
@Throws(IOException::class)
fun kotlinMethodThatThrows(): String {
if (Random.nextBoolean()) {
throw IOException("随机失败")
}
return "成功"
}
6. 静态成员互操作
val result = Utils.format("hello")
class KotlinClass {
companion object {
@JvmStatic
fun staticMethod() = "static"
fun instanceMethod() = "instance"
}
}
7. 默认参数与重载
@JvmOverloads
fun createUser(
name: String,
age: Int = 0,
email: String? = null
): User = User(name, age, email)
8. 属性互操作
class Person(val name: String) {
var age: Int = 0
private set
@JvmField
val id: String = UUID.randomUUID().toString()
}
二、Kotlin 可空性系统
1. 可空类型基础
var nonNullString: String = "Hello"
var nullableString: String? = "Hello"
nullableString = null
val length1: Int? = nullableString?.length
val length2: Int = nullableString?.length ?: 0
val length3: Int = nullableString!!.length
2. 安全调用操作符 ?.
class Company(val name: String, val address: Address?)
class Address(val street: String, val city: City?)
class City(val name: String, val country: String?)
fun getCountryName(company: Company?): String? {
return company?.address?.city?.country
}
company?.address?.let { address ->
println("公司地址: ${address.street}")
}
val result = nullableString?.let {
it.uppercase().reversed()
}
3. Elvis 操作符 ?:
val name: String = nullableString ?: "默认值"
val configPath = System.getenv("CONFIG_PATH")
?: System.getProperty("config.path")
?: "./config.json"
fun processUser(user: User?) {
val validUser = user ?: return
val name = user.name ?: throw IllegalArgumentException("用户名为空")
}
val cityName = user
?.address
?.city
?.name ?: "未知城市"
4. 非空断言 !!
val nullableValue: String? = getNullableValue()
val dangerous: String = nullableValue!!
fun processValues(values: List<String?>): List<String> {
return values.filterNotNull()
return values.map { it!! }
}
fun safeUseOfBangBang() {
val value: String? = computeValue()
if (value != null) {
val length = value.length
}
check(value != null)
val safeValue = value!!
}
5. 安全转换 as?
val anyValue: Any = "String"
val stringValue: String? = anyValue as? String
val intValue: Int? = anyValue as? Int
fun processInput(input: Any) {
val string = input as? String
if (string != null) {
println("字符串长度: ${string.length}")
} else {
println("输入不是字符串")
}
}
6. 集合的可空性
val nullableList: List<String?> = listOf("a", null, "c", null)
val nonNullList: List<String> = nullableList.filterNotNull()
val lengths: List<Int> = nullableList.mapNotNull { it?.length }
val nullableMap: Map<String, String?> = mapOf(
"key1" to "value1",
"key2" to null
)
val value = nullableMap["key2"] ?: "默认值"
7. 平台类型与可空性注解
fun useJavaService(service: JavaService) {
val name: String = service.getName()
val result: String? = service.findById("123")
val platformResult = service.unannotatedMethod()
val safeResult1: String? = platformResult
val safeResult2: String = platformResult ?: ""
}
8. 扩展函数处理可空性
fun String?.safeLength(): Int = this?.length ?: 0
fun String?.isNullOrBlankExtended(): Boolean {
return this == null || this.isBlank()
}
val nullableString: String? = null
println(nullableString.safeLength())
println(nullableString.isNullOrBlankExtended())
inline fun <T : Any, R> T?.ifNotNull(block: (T) -> R): R? {
return if (this != null) block(this) else null
}
val result = nullableString.ifNotNull {
it.uppercase()
}
9. 泛型中的可空性
class Box<T>(val value: T)
val box1: Box<String> = Box("hello")
val box2: Box<String?> = Box(null)
class NonNullBox<T : Any>(val value: T)
val box3: NonNullBox<String> = NonNullBox("hello")
interface Producer<out T> {
fun produce(): T
}
interface Consumer<in T> {
fun consume(item: T)
}
val stringProducer: Producer<String> = object : Producer<String> {
override fun produce(): String = "Hello"
}
val nullableStringProducer: Producer<String?> = stringProducer
10. 实际场景中的可空性处理
场景1:API 响应解析
data class ApiResponse(
val success: Boolean,
val data: UserData?,
val error: String?
)
data class UserData(
val id: String,
val name: String?,
val email: String
)
fun processApiResponse(response: ApiResponse): Result<User> {
return if (response.success) {
val data = response.data ?: return Result.failure("数据为空")
val name = data.name ?: "匿名用户"
val user = User(data.id, name, data.email)
Result.success(user)
} else {
val error = response.error ?: "未知错误"
Result.failure(error)
}
}
场景2:配置管理
class Configuration {
private val config = mutableMapOf<String, Any?>()
fun <T : Any> get(key: String, defaultValue: T): T {
return (config[key] as? T) ?: defaultValue
}
fun <T : Any> getOrThrow(key: String): T {
return config[key] as? T
?: throw IllegalArgumentException("配置项 '$key' 不存在或类型不匹配")
}
fun getStringOrNull(key: String): String? {
return config[key] as? String
}
}
场景3:数据库实体
@Entity
data class UserEntity(
@Id
val id: String,
@Column(nullable = false)
val username: String,
@Column
val displayName: String?,
@Column
val avatarUrl: String?,
@Column(nullable = false)
val createdAt: Instant,
@Column
val deletedAt: Instant?
) {
val isActive: Boolean
get() = deletedAt == null
}
11. 可空性的性能优化
fun processUser(user: User?) {
if (user != null) {
println(user.name)
println(user.email)
println(user.age)
}
user?.let {
println(it.name)
println(it.email)
println(it.age)
}
val nonNullUser = user ?: return
println(nonNullUser.name)
println(nonNullUser.email)
println(nonNullUser.age)
}
inline fun <T, R> T?.ifNotNull(block: (T) -> R): R? {
return if (this != null) block(this) else null
}
fun describeUser(user: User?): String {
return when {
user == null -> "用户不存在"
user.name == null -> "匿名用户"
user.age < 18 -> "未成年用户: ${user.name}"
else -> "成年用户: ${user.name}"
}
}
三、互操作与可空性的结合
1. 在混合项目中处理可空性
class InteropService {
@JvmStatic
fun findUser(id: String): User? = database.findUser(id)
@JvmOverloads
fun createUser(
name: String,
email: String? = null,
age: Int = 0
): User = User(name, email, age)
}
fun processJavaCollection(javaList: List<String>): List<String> {
return javaList.filterNotNull()
}
2. 互操作中的常见陷阱
val person = Person()
val isActive = person.active
person.active = true
fun kotlinProcess(input: String, option: String? = null) {
}
@JvmName("processWithOption")
fun kotlinProcess(input: String, option: String? = null) { ... }
3. 创建互操作友好的库
class InteropFriendlyLibrary {
companion object {
@JvmStatic
fun staticMethod() = "static"
}
fun instanceMethod() = "instance"
@JvmOverloads
fun complexMethod(
required: String,
optional1: Int = 0,
optional2: Boolean = false
) = "$required-$optional1-$optional2"
fun getValueOrNull(key: String): String? = storage[key]
fun getValueOrDefault(key: String, defaultValue: String): String {
return getValueOrNull(key) ?: defaultValue
}
}
object StringUtils {
fun String.customExtension(): String = this.uppercase()
fun customExtension(str: String): String = str.customExtension()
}
四、最佳实践总结
互操作性最佳实践:
- 使用注解:在 Java 代码中使用
@Nullable 和 @NotNull 注解
- 提供静态成员:使用
@JvmStatic 和 @JvmField 为 Java 提供便利
- 处理默认参数:使用
@JvmOverloads 为 Java 生成重载方法
- 命名避免冲突:使用
@JvmName 解决命名冲突
- 集合转换:明确转换 Java 和 Kotlin 集合类型
可空性最佳实践:
- 优先使用非空类型:只在必要时使用可空类型
- 避免使用
!!:除非你能 100% 确定不为 null
- 善用 Elvis 操作符:提供有意义的默认值
- 使用安全调用链:
a?.b?.c?.d
- 尽早验证:在函数入口处验证参数
- 使用智能转换:Kotlin 编译器能跟踪空检查后的类型
- 为 Java 调用者考虑:提供非空和可空版本的 API
混合项目指导:
- 统一编码规范:团队内统一空安全处理方式
- 渐进迁移:逐步将 Java 代码迁移到 Kotlin
- 测试覆盖:确保互操作边界有充分的测试
- 代码审查:特别注意混合语言边界的代码
- 文档化约定:记录团队在处理互操作和可空性时的约定
通过合理利用 Kotlin 的互操作性和可空性系统,可以在保持与 Java 生态兼容的同时,享受到 Kotlin 类型安全带来的好处。