1. 概述
在本教程中,我们将回顾 Java 中的 static final
变量,并学习如何在 Kotlin 中实现类似的功能。
在 Java 中,使用 static final
声明的变量通常用于表示常量。而在 Kotlin 中,我们有多种方式可以达到相同的目的。
2. 在 Kotlin object 中定义常量
首先,我们来看在 Kotlin 的 object
中定义常量的方式:
object TestKotlinConstantObject {
const val COMPILE_TIME_CONST = 10
val RUN_TIME_CONST: Int
init {
RUN_TIME_CONST = TestKotlinConstantObject.COMPILE_TIME_CONST + 20
}
}
在这个例子中,我们使用:
const val
定义编译期常量(compile-time constant)val
定义运行期常量(run-time constant)
在 Kotlin 中可以直接像访问 Java 的 static final
变量一样使用它们:
// Kotlin
assertEquals(10, TestKotlinConstantObject.COMPILE_TIME_CONST)
assertEquals(30, TestKotlinConstantObject.RUN_TIME_CONST)
⚠️ 注意: 我们不能在 Java 中直接使用 TestKotlinConstantObject.RUN_TIME_CONST
。
因为 Kotlin 中的 val
变量默认不会暴露为 Java 可访问的 public static final
字段。
在 Java 中要访问 RUN_TIME_CONST
,需要通过 INSTANCE
调用 getter 方法:
// Java
assertEquals(10, TestKotlinConstantObject.COMPILE_TIME_CONST);
assertEquals(30, TestKotlinConstantObject.INSTANCE.getRUN_TIME_CONST());
✅ 解决方案: 使用 @JvmField
注解可以将 val
变量暴露为 Java 中的 public static final
字段:
@JvmField val JAVA_STATIC_FINAL_FIELD = 20
这样就可以像 Java 的 static final
一样访问:
// Kotlin
assertEquals(20, TestKotlinConstantObject.JAVA_STATIC_FINAL_FIELD)
// Java
assertEquals(20, TestKotlinConstantObject.JAVA_STATIC_FINAL_FIELD);
📌 另外,我们也可以使用 @JvmStatic
,但需要注意:
@JvmStatic
会为属性的 getter/setter 生成静态方法,但并不会将字段本身变为static final
。
3. 在 Kotlin class 的 companion object 中定义常量
在 Kotlin 的类中定义常量,通常放在 companion object
中:
class TestKotlinConstantClass {
companion object {
const val COMPANION_OBJECT_NUMBER = 40
}
}
调用方式与 object
类似:
// Kotlin
assertEquals(40, TestKotlinConstantClass.COMPANION_OBJECT_NUMBER)
// Java
assertEquals(40, TestKotlinConstantClass.COMPANION_OBJECT_NUMBER);
4. 在 .kt 文件顶层定义常量
除了在 object
或 companion object
中定义,我们还可以直接在 Kotlin 文件的顶层定义常量。
例如:
// KotlinFile.kt
package com.baeldung.constant
const val VALUE_IN_KT_FILE = 42
val greeting = "Hello"
在 Kotlin 中可以直接使用:
// Kotlin
assertEquals(42, VALUE_IN_KT_FILE)
assertEquals("Hello", greeting)
但在 Java 中不能直接访问这些变量,因为 Java 要求变量必须属于某个类或对象。
编译器会将 .kt
文件编译为一个类,类名由文件名决定:
- 文件名首字母大写:
myKotlin.kt
→MyKotlinKt
- 首字符非法则加
_
:7myKotlin.kt
→_7myKotlinKt
- 替换
.
为_
:my.Kotlin.kt
→My_KotlinKt
因此在 Java 中访问方式如下:
assertEquals(42, KotlinFileKt.VALUE_IN_KT_FILE);
assertEquals("Hello", KotlinFileKt.getGreeting());
📌 和 object
中一样:
const val
可以直接访问val
需要通过 getter 方法访问
✅ 优化建议: 使用 @file:JvmName
注解自定义编译后的类名,提高可读性和可控性:
// KotlinFileWithAnnotation.kt
@file:JvmName("NiceKotlinUtil")
package com.baeldung.constant
const val VALUE_IN_KT_FILE_WITH_ANNOTATION = 4242
@JvmField val greetingFromFileWithAnnotation = "Hello world"
这样在 Java 中就可以这样访问:
// Java
assertEquals(4242, NiceKotlinUtil.VALUE_IN_KT_FILE_WITH_ANNOTATION);
assertEquals("Hello world", NiceKotlinUtil.greetingFromFileWithAnnotation);
5. 总结
本文我们介绍了在 Kotlin 中实现 Java static final
变量的几种方式:
方式 | 说明 | Java 可见性 |
---|---|---|
const val |
编译期常量 | ✅ |
val |
运行期常量 | ❌(需通过 INSTANCE.getXXX() ) |
@JvmField val |
将变量暴露为 Java 的 public static final |
✅ |
companion object 中的 const val |
类似 Java 静态常量 | ✅ |
.kt 文件顶层 const val |
默认生成类名 FilenameKt |
✅ |
.kt 文件加 @file:JvmName + @JvmField |
更加灵活和 Java 友好 | ✅ |
📌 踩坑提醒:
- Kotlin 的
val
不会自动暴露为 Java 的static final
- 文件名转换规则复杂,建议用
@file:JvmName
显式控制类名 - 需要 Java 可见的常量,记得加
@JvmField
完整的代码示例可在 GitHub 上找到。