例子
lateinit 是kotlin的关键字,和var一起使用,用来声明一个不可空的不用立刻赋值的变量。通常用法是:
private lateinit var mScore: TextView
fun init(view: View) {
mScore = view.findViewById(R.id.score)
}
fun updateScore(score: Int) {
mScore.text = "分数为:$score"
}
相对于val和var而言,有几个好处:
- 不用立刻赋值;
- 跟 val 相比,可以反复赋值;
- 跟延后赋值的 var 相比,使用时不用到处加 ? 或 !!。
Java实现
我们先将上面例子中的代码用java写下:
private TextView mScore;
public void init(View view) {
mScore = view.findViewById(id.score);
}
public final void updateScore(int score) {
mScore.setText((CharSequence)("分数为:" + score));
}
再通过 Android Studio 的 show kotlin bytecode 工具,将例子中的kotlin转化成 java ,会发现这个代码跟我们用java代码写的挺像的。
private TextView mScore;
public final void init(@NotNull View view) {
Intrinsics.checkParameterIsNotNull(view, "view");
View var10001 = view.findViewById(id.score);
Intrinsics.checkExpressionValueIsNotNull(var10001, "view.findViewById(R.id.score)");
this.mScore = (TextView)var10001;
}
public final void updateScore(int score) {
TextView var10000 = this.mScore;
if (var10000 == null) {
Intrinsics.throwUninitializedPropertyAccessException("mScore");
}
var10000.setText((CharSequence)("分数为:" + score));
}
去掉烦人的Intrinsics 的 checkParameterIsNotNull 、checkExpressionValueIsNotNull、throwUninitializedPropertyAccessException,那就是完全一样了!
可以推断 lateinit var 是一个为了照顾我们java写法习惯而创建关键词。
深入一点
看下 kotlin 生成的 java updateScore()方法,在 mScore 变量使用前,会先检查下是否为空,如果为空,会抛出异常:UninitializedPropertyAccessException。
public static void throwUninitializedPropertyAccessException(String propertyName) {
throwUninitializedProperty("lateinit property " + propertyName + " has not been initialized");
}
这就是我们在使用lateinit时会遇到的:lateinit property *** has not been initialized 异常。
因此:在使用lateinit var声明的变量前,一定保证变量被初始化了!
那有没有能检测 lateinit var的变量是否被初始化的方法呢?
答案是有,那就是使用lateinit var 变量的 isInitialized 属性。
fun updateScore(score: Int) {
if (::mScore.isInitialized) {
mScore.text = "分数为:$score"
}
}
转化成java是:
public final void updateScore(int score) {
if (((Test)this).mScore != null) {
TextView var10000 = this.mScore;
if (var10000 == null) {
Intrinsics.throwUninitializedPropertyAccessException("mScore");
}
var10000.setText((CharSequence)("分数为:" + score));
}
}
可以看到isInitialized的实现其实是做了判空,如果不为空,才去使用。果然比较符合java的习惯。
建议
避免使用lateinit var。因为非常容易出现“lateinit property *** has not been initialized”的异常。如果到处使用isInitialized,又会让代码会变得冗余。推荐 val 和var 搞定一切哈。