尝试在空对象引用上调用接口方法'java.lang.Object kotlin.Lazy.getValue()'

18

非常抱歉我的问题很愚蠢。

我已经谷歌了这个问题,但答案对我没有用。

这是我的代码。

  data class Article(val id: Long, val title: String, val ingress: String, val image: String,
                       val dateTime: String, val tags: List<String>, val content: List<Item>, val created: Long, val changed: Long) {

        @delegate:Transient
        val formatDateString: String by lazy {
            val sdf = SimpleDateFormat("dd.MM.yyyy HH.mm")
            val date: Date = sdf.parse(dateTime)
            var time: String?
            if (date.year == Date().year) {
                time = SimpleDateFormat("dd MM,HH:mm", Locale.US).format(date)
            } else {
                time = SimpleDateFormat("dd MM yyyy,HH:mm", Locale.US).format(date)
            }
            time!!
        }

    }

当我尝试获取formatDateString的值时,会出现以上异常。

代码如下访问formatDateString

  override fun getCustomAdapter(): BaseQuickAdapter<CarListResponseBody.Article, BaseViewHolder> {
        return object : BaseQuickAdapter<CarListResponseBody.Article, BaseViewHolder>( R.layout.app_item_cars_list,dataList) {
            override fun convert(helper: BaseViewHolder, item: CarListResponseBody.Article) {
                helper.setText(R.id.tv_ingress,item.ingress)
                helper.setText(R.id.tv_date_time,item.formatDateString)
                helper.setText(R.id.tv_title,item.title)
                ImageLoadUtil.loadImgToView(context,item.image,helper.getView(R.id.img_car),null)
            }
        }
    }

我认为这个异常是由Gson引起的,但我不知道原因。

3个回答

20

我不是专业人士,但我想告诉你我发现了什么。让我们创建一个简单的类来看看 Kotlin 中的委托是如何工作的。以防万一,我正在使用 Kotlin 1.3.70。这是我们的简单类:

class Test {
    val test by lazy { "test" }
}

在Idea或Android Studio中,您可以检查字节码。但至少对我来说,阅读JVM字节码非常困难 :) 让我们将其转换为类似于Java的内容:

...
public final class Test {
   @NotNull
   private final Lazy test$delegate;

   @NotNull
   public final String getTest() {
      Lazy var1 = this.test$delegate;
      Object var3 = null;
      boolean var4 = false;
      return (String)var1.getValue();
   }

   public Test() {
      this.test$delegate = LazyKt.lazy((Function0)null.INSTANCE);
   }
}
...

如您所见,Kotlin为lazy代理创建了一个特殊的字段,并在构造函数中进行初始化。问题是Gson使用UnsafeAllocator,不调用构造函数。您可以在此处阅读更多相关信息。因此,当您反序列化Article对象时,它包含null,而不是适当的lazy代理。这就是为什么在尝试调用该代理时出现空指针异常的原因。


1
谢谢,你有什么想法来解决这个问题吗?或者对于formatDateString有更好的解决方案? - Cyrus
2
作为一个快速解决方案,您可以编写一个扩展函数。但是,如果您经常调用formatDateString,它会导致一些开销。此外,您可以使用包装器来包含formatDateString。但是,使用起来可能不是非常方便。也许您可以考虑从Gson迁移到Moshi或kotlinx.serialization,但这需要相当大的工作量。 - Andrei Tanana

4

在使用Gson未填充的属性时,我遇到了同样的问题。我使用了以下解决方法:

var myString: String? = null
    get() {
        if (field == null) {
            field = "myInitialValue"
        }
        return field
    }

我猜在您的例子中应该是这样的:


val formatDateString: String? = null
    get() {
        if (field == null) {
            val sdf = SimpleDateFormat("dd.MM.yyyy HH.mm")
            val date: Date = sdf.parse(dateTime)
            var time: String?
            if (date.year == Date().year) {
                time = SimpleDateFormat("dd MM,HH:mm", Locale.US).format(date)
            } else {
                time = SimpleDateFormat("dd MM yyyy,HH:mm", Locale.US).format(date)
            }
            field = time!!
        }
        return field
    }
        

我不知道这是否是一种不好的做法,但对我来说似乎能胜任工作。


0
我也遇到了异常情况 "java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object kotlin.Lazy.getValue()' on a null object reference",当我尝试在数据类中编写一个方法(用于Gson)时。
data class SomeResponse(
    override val success: Int,
    override val errors: ErrorsResponse?
) {

    data class ErrorsResponse(
        @SerializedName("id")
        val id: Int?
    ) {

        private val errorUtil by lazy { ErrorUtil() }

        fun getErrorMessage(): String? =
            errorUtil.getErrorMessage(id)
    }
}

无论是使用 errorUtil by lazy { ErrorUtil() } 还是 errorUtil = ErrorUtil(),当我们调用 getErrorMessage 时,它都会是 null。它不会很快被初始化。
为了解决这个问题,在调用时初始化变量即可。
private var errorUtil: ErrorUtil? = null

fun getErrorMessage(): String? {
    if (errorUtil == null) errorUtil = ErrorUtil()
    return errorUtil!!.getErrorMessage(id)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接