Kotlin lateinit记者java

时间:2017-03-22 23:14:01

标签: java android kotlin

您好,当我使用Kotlin编程Android时,我在代码中看到了lateinit。 java中的等价物是什么?如何将此代码从Kotlin更改为Java?

public class MyTest {
    lateinit var subject: TestSubject
}

3 个答案:

答案 0 :(得分:5)

Kotlin中有

lateinit,因此您可以在变量上创建不可为空的类型,这些变量在创建包含它们的类时无法初始化。

使用您的示例,如果您未使用lateinit,则必须使subject为空,因为必须使用值进行初始化。

public class MyTest {
    var subject: TestSubject? = null
}

这会强制您在每次使用时都进行空检查,这很难看,因此您可以将其标记为lateinit

在Java中,你没有真正遇到这个问题,因为一切都可以为空,并且声明一个未初始化的字段并不特别:

public class JavaTest {
    TestSubject subject;
}

这会将subject初始化为null,因此它实际上等同于非lateinit Kotlin示例。

Kotlin中的lateinit版本与Java版本之间唯一真正的区别是,当您尝试访问Kotlin中的未初始化属性时,您会获得更具体的异常,即UninitializedPropertyAccessException,使调试更容易,而不是寻找通用NullPointerException的原因。

如果你真的想要这种稍微不同的行为,你可以将Java属性包装在某种包装器中,但我不认为这样做会有语法上的开销。一个非常基本的(不是线程安全的)方法是:

拥有属性的通用包装类:

public class Property<T> {

    private T value = null;

    public T get() {
        if (value == null)
            throw new UninitializedPropertyAccessException("Property has not been initialized");
        return value;
    }

    public void set(T value) {
        if (value == null)
            throw new IllegalArgumentException("Value can't be null");
        this.value = value;
    }

}

在你的课程中使用这个包装器:

public class JavaTest {
    Property<TestSubject> subject = new Property<>();
}

然后这个用法会给你未初始化的异常:

JavaTest test = new JavaTest();
test.subject.get();

这一个可以正常运行:

JavaTest test = new JavaTest();
test.subject.set(new TestSubject());
test.subject.get();

编辑:这与lateinit在Kotlin中的工作方式非常相似,如果您将示例的字节码反编译为Java,这就是您所获得的:

public final class MyTest {
   @NotNull
   public TestSubject subject;

   @NotNull
   public final TestSubject getSubject() {
      TestSubject var10000 = this.subject;
      if(this.subject == null) {
         Intrinsics.throwUninitializedPropertyAccessException("subject");
      }
      return var10000;
   }

   public final void setSubject(@NotNull TestSubject var1) {
      Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
      this.subject = var1;
   }
}

基本上,编译器将用于检查属性访问的代码放在类本身内(+使用一些辅助方法)而不是使用包装器,这样更有效。

答案 1 :(得分:0)

Java中没有这样的属性会延迟属性的初始化。但我认为Kotlin和Java中的行为非常相似。

这是我的两分钱:

在Kotlin中,在初始化之前访问lateinit属性会抛出一个特殊的异常,它可以清楚地识别被访问的属性以及它尚未初始化的事实。 (参考:https://kotlinlang.org/docs/reference/properties.html)。

在Java中,还抛出NullPointerException(NPE),表示正在访问的属性为Null。

唯一的区别可能是,NPE是运行时异常。

答案 2 :(得分:0)

有许多examples here。就您而言:

public class MyTest {
    lateinit var subject: TestSubject
}

它将编译为以下Java代码:

import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

public final class MyTest {
  @NotNull public TestSubject subject;

  @NotNull
  public final TestSubject getSubject() {
    TestSubject testSubject = this.subject;
    if (testSubject == null) {
      Intrinsics.throwUninitializedPropertyAccessException((String) "subject");
    }
    return testSubject;
  }

  public final void setSubject(@NotNull TestSubject testSubject) {
    Intrinsics.checkParameterIsNotNull((Object) testSubject, (String) "<set-?>");
    this.subject = testSubject;
  }
}