未经检查的强制转换:尝试在同一方法中将 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"

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

【问题讨论】:

这个函数应该如何使用?在调用时是否知道它应该返回什么(MutableList&lt;Int&gt;MutableList&lt;String&gt;)? 我认为泛型在这里不合适。您需要知道类型是 Int 还是 String 才能正确处理。您可以将类型具体化,但由于只有两种可接受的类型,并且它们的处理方式不同,因此您应该只有两个单独的函数,一个用于 Ints,一个用于 Strings。 我使用此函数的目标是在 mutableList(IntString)中添加先前保存的项目(存储为 String),这就是为什么我将项目转换为 T (希望这个分别是IntString)。最后,也许写2个不同的函数是最好的选择。但是,如果我们假设该函数可以被任何类型的列表调用,我如何将 String(保存的数据)转换为 T(作为参数传递的列表元素的类型)? 【参考方案1】:

在 Kotlin 中没有 union types。 所以你不能将T 描述为IntString,因此你不能将MutableList&lt;T&gt; 描述为MutableList&lt;Int&gt;MutableList&lt;String&gt;

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

我建议在这里使用额外的函数参数,将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> 未经检查的强制转换

如何在Clojure中将字符强制转换为int?

Kotlin:如何使用列表强制转换:未经检查的强制转换:kotlin.collections.List<Kotlin.Any?> 到 kotlin.colletions.List<W

未经检查的强制转换:'java.io.Serializable'为'java.util.ArrayList “

如何在 C++ 中将字符串转换为 int?