具有合成绑定和可空性的 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
我注意到在使用 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