Kotlin lateinit属性,NPE有危险吗? [英] Kotlin lateinit properties, NPE danger?

查看:58
本文介绍了Kotlin lateinit属性,NPE有危险吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用lateinit属性,以避免使用?进行连续的空检查.操作员.我有很多在getViews()函数中首次分配的View属性.如果没有该功能,我的应用程序将因Kotlin代码中的NPE而崩溃.

I am using lateinit properties in order to avoid continuous null checking with the ? operator. I have a lot of View properties that are assigned first time in the getViews() function. If that function weren't there, my application would crash with a NPE, from a Kotlin code.

在我看来,lateinit属性基本上破坏了该语言的不错的null安全功能.我知道它们是在M13中引入的,因为它提供了更好的框架支持,但这值得吗?

In my opinion lateinit properties basically ruin the nice null safety features of the language. I know they are introduced in M13 because of better framework support, but does it worth it?

还是我在这里想念东西?

Or am I missing something here?

这是代码:

package com.attilapalfi.exceptional.ui

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import com.attilapalfi.exceptional.R
import com.attilapalfi.exceptional.dependency_injection.Injector
import com.attilapalfi.exceptional.model.Exception
import com.attilapalfi.exceptional.model.ExceptionType
import com.attilapalfi.exceptional.model.Friend
import com.attilapalfi.exceptional.persistence.*
import com.attilapalfi.exceptional.rest.ExceptionRestConnector
import com.attilapalfi.exceptional.ui.helpers.ViewHelper
import com.attilapalfi.exceptional.ui.question_views.QuestionYesNoClickListener
import com.google.android.gms.maps.MapView
import java.math.BigInteger
import javax.inject.Inject

public class ShowNotificationActivity : AppCompatActivity(), QuestionChangeListener {
    @Inject
    lateinit val exceptionTypeStore: ExceptionTypeStore
    @Inject
    lateinit val friendStore: FriendStore
    @Inject
    lateinit val imageCache: ImageCache
    @Inject
    lateinit val metadataStore: MetadataStore
    @Inject
    lateinit val viewHelper: ViewHelper
    @Inject
    lateinit val exceptionInstanceStore: ExceptionInstanceStore
    @Inject
    lateinit val exceptionRestConnector: ExceptionRestConnector
    @Inject
    lateinit val questionStore: QuestionStore
    private lateinit var sender: Friend
    private lateinit var exception: Exception
    private lateinit var exceptionType: ExceptionType
    private lateinit var exceptionNameView: TextView
    private lateinit var exceptionDescView: TextView
    private lateinit var senderImageView: ImageView
    private lateinit var senderNameView: TextView
    private lateinit var sendDateView: TextView
    private lateinit var mapView: MapView
    private lateinit var questionText: TextView
    private lateinit var noButton: Button
    private lateinit var yesButton: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_show_notification)
        Injector.INSTANCE.applicationComponent.inject(this)
        questionStore.addChangeListener(this)
        getModelFromBundle()
        getViews()
        loadViewsWithData()
    }

    override fun onDestroy() {
        super.onDestroy()
        questionStore.removeChangeListener(this)
    }

    private fun getModelFromBundle() {
        val bundle = intent.extras
        val instanceId = BigInteger(bundle.getString("instanceId"))
        exception = exceptionInstanceStore.findById(instanceId)
        sender = friendStore.findById(exception.fromWho)
        exceptionType = exceptionTypeStore.findById(exception.exceptionTypeId)
    }

    private fun getViews() {
        exceptionNameView = findViewById(R.id.notif_full_exc_name) as TextView
        exceptionDescView = findViewById(R.id.notif_exc_desc) as TextView
        senderImageView = findViewById(R.id.notif_sender_image) as ImageView
        senderNameView = findViewById(R.id.notif_sender_name) as TextView
        sendDateView = findViewById(R.id.notif_sent_date) as TextView
        mapView = findViewById(R.id.notif_map) as MapView
        questionText = findViewById(R.id.notif_question_text) as TextView
        noButton = findViewById(R.id.notif_question_no) as Button
        yesButton = findViewById(R.id.notif_question_yes) as Button
    }

    private fun loadViewsWithData() {
        exceptionNameView.text = exceptionType.prefix + "\n" + exceptionType.shortName
        exceptionDescView.text = exceptionType.description
        imageCache.setImageToView(sender, senderImageView)
        senderNameView.text = viewHelper.getNameAndCity(exception, sender)
        sendDateView.text = exception.date.toString()
        loadQuestionToViews()
    }

    private fun loadQuestionToViews() {
        if (exception.question.hasQuestion) {
            showQuestionViews()
        } else {
            hideQuestionViews()
        }
    }

    private fun showQuestionViews() {
        questionText.text = exception.question.text
        val listener = QuestionYesNoClickListener(exception, exceptionRestConnector, noButton, yesButton)
        noButton.setOnClickListener(listener)
        yesButton.setOnClickListener(listener)
    }

    private fun hideQuestionViews() {
        questionText.visibility = View.INVISIBLE
        noButton.visibility = View.INVISIBLE
        yesButton.visibility = View.INVISIBLE
    }

    override fun onQuestionsChanged() {
        onBackPressed()
    }
}

推荐答案

M13之前的Delegates.notNull实际上可以实现 lateinit 的相同基本功能.

The same basic feature of lateinit was actually possible with Delegates.notNull prior to M13.

还有其他功能也可以让您绕过可空性实施. 运算符将将可为空的值转换为非空值.

There's other features that also allow you to bypass the nullability enforcements. The !! operator will convert a nullable value into a non-null value.

重点不是严格要求可空性约束,而是使可空性成为语言中非常明确的一部分.每次使用 lateinit !! 时,您都在做出有意识的决定,以保留可空性约束的安全性,希望这是有充分理由的.

The point is not to strictly require nullability constraints, but to make nullability a very explicit part of the language. Every time you use lateinit or !! you are making a conscious decision to leave the safety of the nullability constraints, hopefully with good reason.

根据经验,最好尽可能避免使用 lateinit !! 和什至?(可为空).

As a rule of thumb it is best to avoid lateinit, !!, and even ? (nullable) as much as possible.

很多时候,您可以使用 惰性 委托来避免 lateinit 会使您陷入非空值的领域(M14现在禁止将带有 lateinit 的值用于vals).

A lot of the time you can use a lazy delegate to avoid lateinit which can keep you in the realm of non-null vals (M14 now prohibits using vals with lateinit).

您链接到的代码包含许多Lateinit视图.您可以通过执行以下操作将它们保留为非空值:

The code you linked to includes a lot of lateinit views. You could keep these as non-null vals by doing something like this:

private val mapView: MapView by lazy { findViewById(R.id.notif_map) as MapView }

这将在第一次使用mapView时初始化该值,此后使用先前初始化的值.需要注意的是,如果您在调用 setContentView 之前尝试使用mapView,这可能会中断.但是,这似乎没什么大不了的,并且您已经获得了初始化就在声明旁边的好处.

This will initialize the value the first time mapView is used and use the previously initialized value thereafter. The caveat is that this could break if you tried to use the mapView before the call to setContentView. However, that doesn't seem like that big of a deal and you've gained the benefit that your initialization is right next to your declaration.

这是kotterknife库用于实现视图注入.

This is what the kotterknife library used to achieve view injection.

这篇关于Kotlin lateinit属性,NPE有危险吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆