介绍
val
是定义只读(Read-only)局部变量时使用的关键字,它定义的变量只能被赋值一次,相当于 Java 中修饰变量的 final 关键字。
val
属性可以在运行时初始化,因此可以将函数或某个类赋值给 val 变量。
const val
是编译时常量(Compile-Time Constants),如果只读的值在编译时已知,使用 const
修饰符将其标记为编译时常量。
const val
定义的常量需要满足以下三个要求:
- 它必须是顶级属性(top-level property)、
object
或 companion
对象的成员。
- 它必须使用 String 类型或基础类型的值进行初始化。
- 它不能是自定义 getter
从 Kotlin 1.1 开始,Kotlin 编译器会将 const val
值内联到使用它们的位置,将常量的引用替换为它的实际值. 但是常量对应的域变量不会被删除, 因此可以通过使用反射与它进行交互.
val 和 const val 的区别
我们可以通过 Kotlin 生成的编译后的字节码,查看两者之间区别。
例如在 PropertiesSample.Kt Kotlin 文件中定义一个只读属性和一个编译时常量:
package cn.itmob.sample.properties
val domainName: String = "itmob.cn"
const val DOMAIN_NAME: String = "itmob.cn"
反编译 Kotlin 代码生成的类文件:
public final class PropertiesKt {
@NotNull
private static final String domainName = "itmob.cn";
@NotNull
public static final String DOMAIN_NAME = "itmob.cn";
@NotNull
public static final String getDomainName() {
return domainName;
}
}
两个值都定义为了 final
,唯一的区别是 const val
被声明为 public
,但 val
是 private
而且定义了一个 getter。
在 Java 类中它们的用法:
public class PropertiesSample {
public static void main(String[] args) {
System.out.println(PropertiesKt.getDomainName());
System.out.println(PropertiesKt.DOMAIN_NAME);
}
}
在 Kotlin 中的用法:
package cn.itmob.sample.properties
fun main() {
println(domainName)
println(DOMAIN_NAME)
}
反编译后是这样的:
public final class PropertiesKt {
public static final void main() {
String var0 = domainName;
System.out.println(var0);
var0 = "itmob.cn";
System.out.println(var0);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
}
const val
值内联到了使用它的位置
总结
与 val
一样,使用 const
关键字定义的变量是不可变的。区别在于 const
用于在编译时已知的变量。