未经检查的转换:尝试在同一方法中将 Int 或 String 转换为 T(通用类型)
Unchecked cast : trying to cast Int or String as T (generic type) in the same method
我对泛型函数还很陌生(在 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"
所以我的目标是知道如何将 String
s 安全地转换为 T(作为参数传递的列表中元素的类型)。现在我收到警告,想知道我所做的是否正确。我还应该添加一个 in
修饰符吗?例如:list: MutableList<in T>
?
Kotlin 中没有 union types。
所以你不能将类型 T
描述为 Int
或 String
,因此你不能将 MutableList<T>
描述为 MutableList<Int>
或 MutableList<String>
但是当你 it.toIntOrNull() ?: it
你得到的甚至不是那个,而是一个可变列表,它可能包含 Int
元素以及 String
元素(因为编译器无法保证该子句将对每个元素以相同的方式解析)。所以编译器试图推断这种类型(它应该是 Int
和 String
的最具体的通用超类型)并且它得到这种可怕的 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}.")
}
}
我对泛型函数还很陌生(在 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"
所以我的目标是知道如何将 String
s 安全地转换为 T(作为参数传递的列表中元素的类型)。现在我收到警告,想知道我所做的是否正确。我还应该添加一个 in
修饰符吗?例如:list: MutableList<in T>
?
Kotlin 中没有 union types。
所以你不能将类型 T
描述为 Int
或 String
,因此你不能将 MutableList<T>
描述为 MutableList<Int>
或 MutableList<String>
但是当你 it.toIntOrNull() ?: it
你得到的甚至不是那个,而是一个可变列表,它可能包含 Int
元素以及 String
元素(因为编译器无法保证该子句将对每个元素以相同的方式解析)。所以编译器试图推断这种类型(它应该是 Int
和 String
的最具体的通用超类型)并且它得到这种可怕的 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}.")
}
}