智能转换为“DrawerLayout!"是不可能的,因为 'drawerLayout' 是一个可变属性,此时可能已更改 [英] Smart cast to 'DrawerLayout!' is impossible, because 'drawerLayout' is a mutable property that could have been changed by this time

查看:39
本文介绍了智能转换为“DrawerLayout!"是不可能的,因为 'drawerLayout' 是一个可变属性,此时可能已更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 android 开发的完全新手,过去两天我一直被这个问题困扰,我的生活中从未感到如此沮丧.先说一点背景故事,

I'm a complete newbie to android development and I've been stuck on this problem for the past two days and I've never felt more frustrated in my life. A little backstory first,

我正在创建最基本的图书图书馆应用程序,并尝试向应用程序添加导航抽屉.在 kotlin 文件中,当我使用 lateinit 声明所有变量(对应于在布局文件中创建的标签)时,它会抛出一个 nullPointerException.出于这个原因,我已经开始像这样声明我的变量:

I'm creating the most basic Book Library app and I was trying to add a Navigation Drawer to the app. Inside the kotlin file, When I declare all the variables (corresponding to the tags created in the layout file) using lateinit, it throws me a nullPointerException. For this reason I've taken to declaring my variables like this:

var drawerLayout: DrawerLayout? = null

这有助于我避免异常.现在来到真正的问题,

which helps me avoid the exception. Now coming to the real problem,

我试图通过这种方式在 onCreate 方法中为我的 actionBarDrawerToggle 创建一个点击侦听器:

I was trying to create a click listener for my actionBarDrawerToggle inside the onCreate method this way:

    val actionBarDrawerToggle = ActionBarDrawerToggle(this@MainActivity, drawerLayout,  R.string.open_drawer, R.string.close_drawer)

    drawerLayout.addDrawerListener(actionBarDrawerToggle)   
    actionBarDrawerToggle.syncState()

出于某种原因,drawerLayout.addDrawerListener(actionBarDrawerToggle)"的 drawerLayout 部分在行以红色下划线表示存在错误.当我运行它时,这是它在构建窗口中显示的错误:

and for some reason, the drawerLayout part of the "drawerLayout.addDrawerListener(actionBarDrawerToggle)" line is underlined in red meaning there's an error. When I run it, this is the error it shows me in build window:

    Smart cast to 'DrawerLayout!' is impossible, because 'drawerLayout' is a mutable property that could have been changed by this time

我不知道如何进行此操作.我已经尝试了很多东西,但都没有奏效.我认为该错误可能与我上面描述的声明方法有关.如果有人可以帮助我就好了

I have no ideo how to proceed with this. I've tried a lot of things and none of them is working. I think the error might have something to do with the declaration method I use that I described above. It would be great if someone could help me out

推荐答案

您已将 drawerLayout 定义为可空的 (DrawerLayout?) var>,这意味着该值可以随时更改,并且该值可以为空.因此,即使您进行空检查以确保它不为空,它也可能随时更改!编译器无法保证调用是安全的.

You've defined drawerLayout as a nullable (DrawerLayout?) var, which means the value can change at any time, and that value can be null. So even if you do a null check to make sure it's not null, that could change at any moment! There's no way for the compiler to guarantee the call is safe.

这里有一些选择,从坏到好:

Here's some options, in order from bad to good:

通过引用drawerLayout!!显式地将变量标记为非空.这是你告诉编译器我知道发生了什么,这很好,相信我".您应该避免这样做,因为这是一个危险信号,您会忽略警告,最终会遇到错误的情况

Explicitly mark the variable as non-null by referencing drawerLayout!!. This is you telling the compiler "I know what's up and it's fine, trust me". You should avoid ever doing this because it's a red flag, you ignore warnings, and you'll end up running into a situation where you're wrong

将非空值复制到您知道不会更改的临时变量中.这是在 Kotlin 中处理可空值的典型方法,您可以使用其中一种作用域函数,例如 let:

Copy the non-null value into a temporary variable, that you know won't change. This is the typical way to handle nullables in Kotlin, and you use one of the scope functions like let:

drawerLayout?.let { drawer -> //  do stuff with drawer }

这是在做一个空检查(使用 ?),如果它不是空的,它会用那个非空调用 let 块价值.而且您知道该值不会在块内更改.我已经调用了变量 drawer 但你可以省略它,默认情况下它被称为 it.还有其他作用域函数,比如 run,做同样事情的方式略有不同.

That's doing a null check (with the ?) and if it's not null, it calls the let block with that non-null value. And you know that value isn't going to change inside the block. I've called the variable drawer but you can omit that, it's called it by default. There's other scope functions like run too, slightly different ways of doing the same thing.

您也可以只调用已检查变量本身,如果您只需要这样做:

You can also just make a call on the checked variable itself, if that's all you need to do:

drawerLayout?.doAThingItsNotNullSoItsFine()

基本上在幕后做同样的事情

which is basically doing the same thing under the hood

首先不要让 drawerLayout 可以为空!它实际上永远不应该为空,对吗?它应该始终存在,并且在您尝试访问它时具有价值?然后保存自己的仇恨和需要一直空检查 - 这就是 lateinit 的用途.

Don't make drawerLayout nullable in the first place! It's never actually supposed to be null, right? It should always exist, and have a value when you try to access it? Then save yourself the aggro and the need to null check it all the time - which is what lateinit is for.

lateinit 允许你声明一个变量而不实际给它一个值.通常你必须这样做,这就是你在这里所做的 - 你使它可以为空,这样你就可以在那里粘贴一个临时的空值.lateinit 是对编译器的承诺,它可以,您还没有提供值,但是在任何尝试访问它之前肯定会设置一个.(这就是为什么它被称为后期初始化!)

lateinit allows you to declare a variable without actually giving it a value. Usually you have to, which is what you've done here right - you've made it nullable, just so you can stick a temporary null in there. lateinit is a promise to the compiler that it's ok, you're not providing a value yet, but you will definitely have set one before anything tries to access it. (That's why it's called late init!)

因此,您遇到的问题是您在尝试读取该值之前实际上并未设置该值.您可以检查它是否已使用 ::drawerLayout.isInitialized 设置,但实际上您应该充分了解您的代码流程,以确保只有在初始化后才能读取.如果您不能保证这一点,那么您可能没有拥有其他东西可能需要访问的变量的值-这是使其可以为空和使用空值处理功能顺利处理

So the problem you were running into was that you hadn't actually set that value before something tried to read it. You can check if it's been set with ::drawerLayout.isInitialized, but really you should understand your code flow enough to ensure that the reading is only possible after the intialisation. If you can't guarantee that, then you have a possible state where you don't have a value for a variable that other stuff might need to access - and that's a good candidate for making it nullable and using the null-handling features to deal with it smoothly

这篇关于智能转换为“DrawerLayout!"是不可能的,因为 'drawerLayout' 是一个可变属性,此时可能已更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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