为什么在我更新我的 Android Studio 后显示错误类型不匹配?

Why is the error Type mismatch displayed after I update my Android Studio?

代码 A 在我的旧版本中运行良好,现在我将 Android Studio 更新为 3.4.2,buildToolsVersion 为“29.0.1”,并且我使用最新的 androidx。

但我收到错误 类型不匹配,您可以看到图 1,我该如何解决?谢谢!

图片 1

代码A

class PreferenceTool<T>(private val context: Context, private val name: String,  private val default: T) {

    private val prefs: SharedPreferences by lazy {       
        context.defaultSharedPreferences        
    }

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T = findPreference(name, default)

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        putPreference(name, value)
    }

    @Suppress("UNCHECKED_CAST")
    private fun findPreference(name: String, default: T): T = with(prefs) {
        val res: Any = when (default) {
            is Long -> getLong(name, default)
            is String -> getString(name, default)
            is Int -> getInt(name, default)
            is Boolean -> getBoolean(name, default)
            is Float -> getFloat(name, default)
            else -> throw IllegalArgumentException("This type can be saved into Preferences")
        }
        res as T
    }

    @SuppressLint("CommitPrefEdits")
    private fun putPreference(name: String, value: T) = with(prefs.edit()) {
        when (value) {
            is Long -> putLong(name, value)
            is String -> putString(name, value)
            is Int -> putInt(name, value)
            is Boolean -> putBoolean(name, value)
            is Float -> putFloat(name, value)
            else -> throw IllegalArgumentException("This type can't be saved into Preferences")
        }.apply()
    }
}

新增内容 1

此外,我发现代码B和代码C都可以正确编译。我不知道为什么。似乎 is String -> getString(name, default) 导致错误。

代码B

@Suppress("UNCHECKED_CAST")
private fun findPreference(name: String, default: T): T = with(prefs) {
    val res: Any = when (default) {
        is Long -> getLong(name, default)
        else -> throw IllegalArgumentException("This type can be saved into Preferences")
    }
    res as T
}

代码C

@Suppress("UNCHECKED_CAST")
private fun findPreference(name: String, default: T): T = with(prefs) {
    val res: Any = when (default) {
        is Long -> getLong(name, default)
        is Int -> getInt(name, default)
        is Boolean -> getBoolean(name, default)
        is Float -> getFloat(name, default)
        else -> throw IllegalArgumentException("This type can be saved into Preferences")
    }
    res as T
}

新增内容 2

似乎 prefs.getString(name, default) return String? 来自 Image 2。不知道Build 29.0.1有没有bug?

图 2

此代码无法编译,因为 SharedPreferences.getString() returns String? 不是 Any 的子类型。为什么之前可以,是不是bug?

这不是错误,而是在 android sdk 中逐渐转向空安全。当 kotlin 成为 android 开发的官方语言时,they started to work on making android sdk kotlin-friendly。特别是,他们向 sdk 代码添加了 @Nullable@NonNull 注释。但是立即这样做会破坏很多项目的编译,因此他们决定给开发人员一些时间来修复他们的代码:

Normally, nullability contract violations in Kotlin result in compilation errors. But to ensure the newly annotated APIs are compatible with your existing code, we are using an internal mechanism provided by the Kotlin compiler team to mark the APIs as recently annotated. Recently annotated APIs will result only in warnings instead of errors from the Kotlin compiler. You will need to use Kotlin 1.2.60 or later.

Our plan is to have newly added nullability annotations produce warnings only, and increase the severity level to errors starting in the following year's Android SDK. The goal is to provide you with sufficient time to update your code.

正如他们所说,他们会提高后续 SDK 的严重性级别,这正是这里发生的事情。但是SharedPreferences的源码没有改,怎么会这样呢?答案是 @RecentlyNullable.

(1) There are some annotations here which are not in the support library, such as @RecentlyNullable and @RecentlyNonNull. These are used only in the stubs to automatically mark code as recently annotated with null/non-null. We do not want these annotations in the source code; the recent-ness is computed at build time and injected into the stubs in place of the normal null annotations.

尽管 SharedPreferences.getString 很久以前就被注释为 @Nullable,显然他们的自动近期性计算决定在 android P 中将其标记为 @RecentlyNullable。证明我们来看看反编译后的代码

Android P:

@RecentlyNullable
String getString(String var1, @RecentlyNullable String var2);

Android 问:

@Nullable
String getString(String var1, @Nullable String var2);