【问题标题】:Kotlin lateinit correspondent javaKotlin lateinit 通讯员 java
【发布时间】:2017-03-22 23:14:00
【问题描述】:

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

public class MyTest {
    lateinit var subject: TestSubject
}

【问题讨论】:

  • 只用java getter,在里面检查是否初始化,如果没有就创建一个

标签: java android kotlin


【解决方案1】:

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

使用您的示例,如果您没有使用 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;
   }
}

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

【讨论】:

    【解决方案2】:

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

    这是我的两分钱:

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

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

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

    【讨论】:

    • UninitializedPropertyAccessException 也是运行时异常。
    【解决方案3】:

    有很多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;
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-02-06
      • 1970-01-01
      • 2016-06-11
      • 2020-08-14
      • 1970-01-01
      • 1970-01-01
      • 2016-10-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多