具有合成绑定和可空性的 Kotlin 视图

Kotlin views with synthetic binding and nullability

我注意到在使用 Kotlin 的合成绑定时,视图 returned 是非空的(Kotlin 将 return View!)。但这对我来说没有多大意义,因为 findCachedViewById 实际上可以 return 为空结果,这意味着视图实际上可以为空。

public View _$_findCachedViewById(int var1) {
  if(this._$_findViewCache == null) {
     this._$_findViewCache = new HashMap();
  }

  View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1));
  if(var2 == null) {
     View var10000 = this.getView();
     if(var10000 == null) {
        return null;
     }

     var2 = var10000.findViewById(var1);
     this._$_findViewCache.put(Integer.valueOf(var1), var2);
  }

  return var2;
}

那么为什么在这种情况下它们不是可选的?为什么 Kotlin 在使用合成绑定时不简单地 return View?,以便开发人员在处理视图时被迫检查可空性?

也许这只是因为我是 Kotlin 的新手,但我认为这有点违反直觉,因为变量不是可选的,但我们仍然应该检查视图是否实际上不为 null。

所以在这种情况下,像下面的代码这样的操作是否有意义?

view?.let {
    // handle non null view here
}

想法是 Android 中的 xml 布局非常静态,为了使用合成视图,您必须直接导入已解析的布局:

import kotlinx.android.synthetic.main.activity_main.*

因此,没有 real-life、non-magic 情况 View 为空。除非你选择了错误的合成布局,否则你将首先 运行.

崩溃

也就是说,如果您在 运行 时间修改视图,删除 Views 等,它当然会中断。但是同样,这不是合成 Views 的默认用法并且需要不同的方法。

我想通了,我总是在 post 我的 :)

之后找到正确的 SO 问题

View 后面的单个感叹号实际上并不意味着视图不能像我预期的那样为 null。

另一个问题的基本上回答了我的确切问题。 View,当使用合成绑定时,实际上可以为空,但我们无法确定,因此只有一个感叹号。

因此可以安全地假设我在上面 post 编辑的代码 - 使用 ?.let{...} 是完全可以接受的处理视图的方式,当您不确定它们在访问它们时是否已经初始化时。

视图可能为空的情况非常罕见,但它可能会发生。

正如您已经指出的,单个感叹号并不意味着它不是空的,而是它是一个 Java 平台类型,编译器不知道它是否可以为空。

我认为你的建议很好,尽管它在 null 的实际情况下默默地失败了,这可能不是你想要的。

假设您尝试在 onCreateView 中调用您的视图,但忘记了它尚未初始化。该片段不会按预期运行,但不会产生有意义的错误来帮助您调试问题。

我仍在尝试自己解决一个或另一个解决方案,但我建议要么显式处理 null 的情况:

view?.let {
    //...
} ?: throwExceptionIfDebugElseLogToCrashlytics()

或者决定这次您真的希望它抛出 NullPointerException,在这种情况下我建议:

view!!.let {
    //...
}

后者不会因为 "should" 不可能的边缘情况而使您的代码膨胀,并且它不会悄无声息地失败,但它仍然让 reader 清楚地知道视图可能是无效的。显然是!!编译器不需要,它只是为了使处理平台类型的选择策略更加明确。

如果您尝试在 activity 或视图的上下文之外或在 lambda 中从侦听器访问视图,那么合成视图绑定实际上可能会发生空指针异常。

问题出在 lambda 中,而 Frantisek 在post这里有关于它的信息: https://whosebug.com/posts/comments/115183445?noredirect=1