Generics Kotlin中的具体化关键字是如何工作的?

Generics Kotlin中的具体化关键字是如何工作的?,generics,kotlin,kotlin-reified-type-parameters,Generics,Kotlin,Kotlin Reified Type Parameters,显然,我在试图理解具体化的关键字的目的 然而,当我不考虑它时,它的效果也一样好。有人想解释一下这什么时候会产生实际的差异吗;医生:什么是“具体化”的好处 具体化的工作原理 您只能将具体化与内联函数结合使用。通过这样做,您可以指示编译器将函数的字节码复制到调用函数的每个位置(编译器“内联”函数)。当使用具体化的类型调用内联函数时,编译器必须能够知道作为类型参数传递的实际类型,以便可以修改生成的字节码以直接使用相应的类。因此,像myVar is T这样的调用在字节码中变成myVar is Stri

显然,我在试图理解
具体化的
关键字的目的

然而,当我不考虑它时,它的效果也一样好。有人想解释一下这什么时候会产生实际的差异吗;医生:什么是“具体化”
的好处
具体化的工作原理
您只能将
具体化
内联
函数结合使用。通过这样做,您可以指示编译器将函数的字节码复制到调用函数的每个位置(编译器“内联”函数)。当使用
具体化的
类型调用
内联
函数时,编译器必须能够知道作为类型参数传递的实际类型,以便可以修改生成的字节码以直接使用相应的类。因此,像
myVar is T
这样的调用在字节码中变成
myVar is String
(如果类型参数是
String


例子 让我们看一个例子,它显示了
具体化的
有多大的帮助。
我们希望为
String
创建一个名为
toKotlinObject
的扩展函数,该函数尝试将JSON字符串转换为普通Kotlin对象,该对象的类型由函数的泛型
T
指定。为此,我们可以使用以下第一种方法:

val oranges = listOf(Orange(), Orange())
fun <T> List<Any>.filterFruit(): List<T> {
    return this.filter { it is T }.map { it as T }          // Error and Warning
}
val oranges = fruits.filterFruit<Orange>()
val oranges = fruits.filterIsInstance<Orange>()
Method method = YourFilenameKt.class.getDeclaredMethod("doSomething", Object.class);
method.invoke("hello", Object.class);
a)没有具体化类型的第一种方法

fun <T> String.toKotlinObject(): T {
      val mapper = jacksonObjectMapper()
                                                        //does not compile!
      return mapper.readValue(this, T::class.java)
}
作为一种解决方法,可以将
T
设置为方法参数,然后将其用作
readValue
的参数。这是通用Java代码中的常见模式。它可以被称为:

inline fun <reified T> myGenericFun()
data class MyJsonType(val name: String)

val json = """{"name":"example"}"""
json.toKotlinObject(MyJsonType::class)
c)科特林方式:
具体化

内联
函数与
具体化
类型参数
T
一起使用,可以以不同方式实现该函数:

inline fun <reified T: Any> String.toKotlinObject(): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(this, T::class.java)
}
重要提示:使用Java
类型为
reified
的内联函数是不可从Java调用的代码。

reified
是在编译时授予使用权限(访问函数内部的
T

例如:

inline fun <reified T:Any>  String.convertToObject(): T{
    val gson = Gson()
    return gson.fromJson(this,T::class.java)
}
inline fun String.convertToObject():T{
val gson=gson()
返回gson.fromJson(this,T::class.java)
}
使用:

val jsonStringResponse = "{"name":"bruno" , "age":"14" , "world":"mars"}"
val userObject = jsonStringResponse.convertToObject<User>()
println(userObject.name)
val jsonStringResponse=“{”name:“bruno”,“age:“14”,“world:“mars”}”
val userObject=jsonStringResponse.convertToObject()
println(userObject.name)
理解
具体化
类型 仿制药

在Kotlin中使用泛型时,我们可以对任何类型的值执行操作
T

fun <T> doSomething(value: T) {
    println("Doing something with value: $value")                 // OK
}
让我们了解这个错误的原因

类型擦除

fun <T> String.toKotlinObject(): T {
      val mapper = jacksonObjectMapper()
                                                        //does not compile!
      return mapper.readValue(this, T::class.java)
}
在上面的代码中,编译器给出了一个错误:
不能使用“T”作为具体化类型参数。改用类。
这种情况发生是因为在编译时,编译器会从函数调用中删除类型参数

例如,如果将函数调用为:

doSomething<String>("Some String")
这称为类型擦除。因此,在运行时(在函数定义中),我们不可能确切地知道
T
代表哪种类型

Java解决方案

Java中此类型擦除问题的解决方案是传递一个附加参数,该参数使用
(在Java中)或
KClass
(在Kotlin中)指定类型:

在上面的代码中,由于
具体化了
类型参数,我们在对类型
T
执行操作时不再出现错误。让我们看看
inline
函数如何使这一神奇变为可能

内联
函数

当我们将函数标记为
inline
时,编译器会将
inline
函数的实际主体复制到调用该函数的任何位置。由于我们将
doSomething()
函数标记为
inline
,因此以下代码:

fun main() {
    doSomething<String>("Some String")
}
因此,上面显示的两个代码片段是等效的

在复制
内联
函数的主体时,编译器还会用函数调用中指定或推断的实际类型参数替换类型参数
T
。例如,请注意类型参数
T
是如何被实际的类型参数
String
替换的


具体化类型的类型检查和类型转换
具体化的
类型参数的主要目的是了解类型参数
T
在运行时表示的确切类型

假设我们有一个不同类型水果的列表:

val fruits = listOf(Apple(), Orange(), Banana(), Orange())
我们想在一个单独的列表中过滤所有的
Orange
类型,如下所示:

val oranges = listOf(Orange(), Orange())
fun <T> List<Any>.filterFruit(): List<T> {
    return this.filter { it is T }.map { it as T }          // Error and Warning
}
val oranges = fruits.filterFruit<Orange>()
val oranges = fruits.filterIsInstance<Orange>()
Method method = YourFilenameKt.class.getDeclaredMethod("doSomething", Object.class);
method.invoke("hello", Object.class);
没有
具体化

为了过滤水果类型,我们可以在
List
上编写一个扩展函数,如下所示:

val oranges = listOf(Orange(), Orange())
fun <T> List<Any>.filterFruit(): List<T> {
    return this.filter { it is T }.map { it as T }          // Error and Warning
}
val oranges = fruits.filterFruit<Orange>()
val oranges = fruits.filterIsInstance<Orange>()
Method method = YourFilenameKt.class.getDeclaredMethod("doSomething", Object.class);
method.invoke("hello", Object.class);
然后像下面这样称呼它:

val oranges = listOf(Orange(), Orange())
fun <T> List<Any>.filterFruit(): List<T> {
    return this.filter { it is T }.map { it as T }          // Error and Warning
}
val oranges = fruits.filterFruit<Orange>()
val oranges = fruits.filterIsInstance<Orange>()
Method method = YourFilenameKt.class.getDeclaredMethod("doSomething", Object.class);
method.invoke("hello", Object.class);

具体化的
参数作为参数传递
reified
修饰符使函数可以将类型参数作为类型参数传递给另一个具有
reified
修饰符的函数:

inline fun <reified T> doSomething() {
    // Passing T as an argument to another function
    doSomethingElse<T>()
}

inline fun <reified T> doSomethingElse() { }
这里的
typeOf()
是一个标准的库函数。如果将函数调用为
getGenericType()
,则上面的
println()
函数将打印
kotlin.collections.List
KType
包括
KClass
、类型参数信息和可空性信息。一旦知道
KType
,就可以对其执行反射


Java互操作性 未声明
具体化
类型参数的
内联
函数可以作为常规Java函数从Java中调用。但是用
具体化的
类型参数声明的那些参数不能从Java调用

即使使用如下反射调用它:

val oranges = listOf(Orange(), Orange())
fun <T> List<Any>.filterFruit(): List<T> {
    return this.filter { it is T }.map { it as T }          // Error and Warning
}
val oranges = fruits.filterFruit<Orange>()
val oranges = fruits.filterIsInstance<Orange>()
Method method = YourFilenameKt.class.getDeclaredMethod("doSomething", Object.class);
method.invoke("hello", Object.class);
您会得到
UnsupportedOperationException:这是f