未经检查的强制转换:尝试在同一方法中将 Int 或 String 强制转换为 T(泛型类型)
Posted
技术标签:
【中文标题】未经检查的强制转换:尝试在同一方法中将 Int 或 String 强制转换为 T(泛型类型)【英文标题】:Unchecked cast : trying to cast Int or String as T (generic type) in the same method 【发布时间】:2020-10-26 10:04:55 【问题描述】:我对泛型函数很陌生(在 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>
?
【问题讨论】:
这个函数应该如何使用?在调用时是否知道它应该返回什么(MutableList<Int>
或 MutableList<String>
)?
我认为泛型在这里不合适。您需要知道类型是 Int 还是 String 才能正确处理。您可以将类型具体化,但由于只有两种可接受的类型,并且它们的处理方式不同,因此您应该只有两个单独的函数,一个用于 Ints,一个用于 Strings。
我使用此函数的目标是在 mutableList(Int
或 String
)中添加先前保存的项目(存储为 String
),这就是为什么我将项目转换为 T (希望这个分别是Int
或String
)。最后,也许写2个不同的函数是最好的选择。但是,如果我们假设该函数可以被任何类型的列表调用,我如何将 String(保存的数据)转换为 T(作为参数传递的列表元素的类型)?
【参考方案1】:
在 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
【讨论】:
感谢您的提示!我不确定是否可以将转换器作为参数传递给函数。没有更多警告,同样简洁易懂!【参考方案2】:这就是您可以使用具体泛型执行此操作的方法,当您需要检查泛型类型时需要这样做。如果您尝试使用 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.")
【讨论】:
有了这段代码,我现在可以更好地理解泛型函数。不幸的是,这并没有像我想做的那样消除 Unchecked cast 警告(即使您使用@Suppress)。 查看编辑。您可以将字符串映射到 T 而不是强制转换。以上是关于未经检查的强制转换:尝试在同一方法中将 Int 或 String 强制转换为 T(泛型类型)的主要内容,如果未能解决你的问题,请参考以下文章
类型安全:从 Object 到 List<MyObject> 的未经检查的强制转换
对象到 ArrayList<String> 未经检查的强制转换
Kotlin:如何使用列表强制转换:未经检查的强制转换:kotlin.collections.List<Kotlin.Any?> 到 kotlin.colletions.List<W