未选中的转换:尝试以相同的方法将 Int 或字符串转换为 T(泛型类型)



我对通用函数(在java和kotlin中)都很陌生。我使用了一个允许我恢复列表的功能(感谢SharedPreferences)。这些列表要么是MutableList<Int>,要么是<String>,要么是<Long>,随便什么...这是我当前使用的代码(我使用list.toString()保存了列表,仅当它不为空时):

fun <T: Any> restoreList(sharedPrefsKey: String, list: MutableList<T>) {
savedGame.getString(sharedPrefsKey, null)?.removeSurrounding("[", "]")?.split(", ")?.forEach { list.add((it.toIntOrNull() ?: it) as T) }
}//"it" is already a String, no need to cast in the "if null" ( ?: ) branch
//warning "Unchecked cast: {Comparable<*> & java.io.Serializable} to T" on "as T"

所以我的目标是知道如何安全地将Strings转换为T(列表中作为参数传递的元素类型)。现在我收到警告,想知道我正在做的事情是否正确。 我是否也应该添加一个in修饰符?例如:list: MutableList<in T>

Kotlin 中没有联合类型。 所以你不能将T类型描述为IntString,因此你不能将MutableList<T>描述为MutableList<Int>MutableList<String>

但是当你这样做it.toIntOrNull() ?: it你得到的甚至不是那个,而是一个可变列表,其中可能包含Int元素以及String元素(因为编译器不能保证此子句将针对每个元素以相同的方式解析)。所以编译器试图推断这种类型(它应该是IntString最具体的常见超类型),它得到了这种可怕的Comparable<*> & java.io.Serializable类型。这T可能是什么施加了如此严重的限制,以至于它实际上变得无用(就像使用MutableList<*>),并且无法使用方差注释进行修复。

我建议在这里使用额外的函数参数,将String(拆分后)转换为所需类型的实例(另请注意,在函数内更改传递的参数是一种代码气味,最好与创建的同一范围内的现有可变列表合并):

fun <T> restoreList(sharedPrefsKey: String, converter: (String) -> T): List<T>? =
savedGame.getString(sharedPrefsKey, null)?.removeSurrounding("[", "]")?.split(", ")?.map { converter(it) }

用法:

val listOfInts = restoreList(sharedPrefKey) { it.toIntOrNull() }
val listOfLongs = restoreList(sharedPrefKey) { it.toLongOrNull() }
val listOfStrings = restoreList(sharedPrefKey) { it }

这就是使用化泛型执行此操作的方式,当您需要检查泛型类型时,泛型是必需的。如果您尝试使用when子句中不支持的类型调用它,则会引发错误。

inline fun <reified T: Any> restoreList(sharedPrefsKey: String, list: MutableList<T>) {
val strings = savedGame.getString(sharedPrefsKey, null)
?.removeSurrounding("[", "]")
?.split(", ")
if (strings == null) {
Log.w("restoreList", "No preferences found for key $sharedPrefsKey.")
return
}
when (T::class) {
String::class -> strings.mapTo(list) { it as T }
Int::class -> strings.mapNotNullTo(list) { it.toIntOrNull() as? T }
Long::class -> strings.mapNotNullTo(list) { it.toLongOrNull() as? T }
// And so on for other types.
else -> error("Unsupported type ${T::class}.")
}
}

最新更新