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