为什么将Activity.findViewById结果类型推断为平台类型?

时间:2018-10-07 17:23:11

标签: android kotlin type-inference

我是Android开发和Kotlin的新手。

我正在关注Kotlin的第一个Android教程,我有这个方法调用:

val editText = findViewById<EditText>(R.id.editText)

findViewByIdandroid.app.Activity的定义为:

@Nullable
public <T extends View> T findViewById(@IdRes int id)

Android Studio向我显示editText的类型为EditText!。我阅读了有关Java互操作性和平台类型的Kotlin文档,但是由于findViewByIdandroid.annotation.Nullable进行了注释,editText是受支持的可空性注释列表中的(according to Kotlin documentation),所以我希望{{{ 1}}类型推断为EditText?

我看到将代码更改为:

val editText = findViewById<EditText?>(R.id.editText)

但是为什么需要这样做,而@Nullable却被忽略了?

2 个答案:

答案 0 :(得分:1)

该方法可以为空,但您的类说明不是。我已经反编译了我的一个APK,发现了以下内容。

这是一个简单的Kotlin方法:

private fun setAppName() {
    val name = view.findViewById<TextView>(R.id.app_name)
    name.text = appInfo.appName
}

您可以将name的规范视为TextView。

该方法在编译时会变成以下内容:

private final void setAppName() {
    TextView textView = (TextView) this.view.findViewById(R.id.app_name);
    Intrinsics.checkExpressionValueIsNotNull(textView, "name");
    AppInfo appInfo = this.appInfo;
    if (appInfo == null) {
        Intrinsics.throwUninitializedPropertyAccessException("appInfo");
    }
    textView.setText(appInfo.getAppName());
}

请注意,它强制findViewById的返回值准确地放入括号中。就您而言,是EditText。

基本上,这使一切混乱。类似于这样做:

val view: View? = findViewById(R.id.whatever)
val castView: TextView = view as TextView

由于Kotlin如何处理类型推断,它有效地绕过了@Nullable

答案 1 :(得分:0)

android.app.Activity上设置的实际注释为@android.annotation.Nullable。不可为空的对应对象是@android.annotation.NonNull。这些不在Kotlin(see documentation)当前支持的“ Android可空性注释”中,而是在软件包com.android.annotationsandroid.support.annotations中声明的。

我打开了问题KT-27566,发现这些注释由于具有源保留而当前被忽略,因此它们在Kotlin编译器处理的编译字节码中不可用。

此外,根据KT-25279看来,似乎还有其他类似的注释,它们在软件包com.android.support中定义,但也存在相同的问题。

我真的不明白,为什么Google会用可空性注释来搞乱(这么多不同的变体,其中有些对Kotlin集成没用,而它们的用处最大。)。