什么是Kotlin中的Reified类型?

reified关键字是用于Kotlin内联函数的,修饰内联函数的泛型,泛型被修饰后,在方法体里,能从泛型拿到泛型的Class对象,这与java是不同的,java需要泛型且需要泛型的Class类型时,是要把Class传过来的,但是kotlin不用了

您必须同意Kotlin是一种很棒的语言,这是因为与其他编程语言相比,它具有独特的功能。这些功能之一是Kotlin中的Reified”关键字。等等,这个奇怪的术语叫“具体化”是什么?别担心,这就是今天博客的主题。在这篇博客中,我们将了解 Reified 类型。那么,让我们开始吧。

开始之前

大多数编程语言都有泛型的概念。泛型用于提供类或函数或某些属性的泛型/常规实现,即您将编写一次实现部分并将其用于各种数据类型。

例如,下面是一个泛型函数的代码:

fun <T> genericsExample(value: T) {
    println(value)
}
fun main() {
    genericsExample<String>("Learning Generics!")
    genericsExample<Int>(100)
}

上面的泛型函数可用于任何类型的变量,即字符串,Int,布尔值等。因此,上述代码的输出将是:

Learning Generics! 
100

现在,在函数中,让我们尝试找到使用的类型,即让我们有一个print语句,如果类型是String,Int,布尔值或其他什么东西,它将打印。genericsExampleTT

fun <T> genericsExample(value: T) {
    println(value)
    prinln("Type of T: ${T::class.java}")
}

上面的代码工作正常吗?不,您将收到错误。我们无法获取有关类型 T 的信息,因为泛型中存在类型擦除问题。因此,如果要访问的类型,那么我们可以将 的类作为参数传递给函数。Cannot use 'T' as reified type parameterTTgenericsExample

fun <T> genericsExample(classType: Class<T>, value: T) {
    println(value)
    println("Type of T: ${classType}")
}
fun main() {
    genericsExample<String>(String::class.java, "Learning Generics!")
    genericsExample<Int>(Int::class.java, 100)
}

但这是最好的方法吗?可以使用这样的样板代码吗?不,绝对不是。

因此,这是Kotlin中Reified关键词的作用。让我们来了解一下。

Kotlin中Reified关键词

为了访问有关类类型的信息,我们在 Kotlin 中使用了一个名为 reified 的关键字。为了使用 reified 类型,我们需要使用内联函数。如果一个函数被标记为 ,那么无论在哪里调用该函数,编译器都会将函数的整个主体粘贴到那里。

inline

因此,以下是使用泛型的上述函数的更新代码(使用简化类型):

inline fun <reified T> genericsExample(value: T) {
    println(value)
    println("Type of T: ${T::class.java}")
}
fun main() {
    genericsExample<String>("Learning Generics!")
    genericsExample<Int>(100)
}

下面是上述代码的输出:

Learning Generics! 
Type of T: class java.lang.String 
100 
Type of T: class java.lang.Integer

那么,引擎盖下发生了什么?为了找到它,我们需要看到上面例子的字节码:

public static final void genericsExample(Object value) {
   int $i$f$genericsExample = 0;
   boolean var2 = false;
   System.out.println(value);
   StringBuilder var10000 = (new StringBuilder()).append("Type of T: ");
   Intrinsics.reifiedOperationMarker(4, "T");
   String var4 = var10000.append(Object.class).toString();
   boolean var3 = false;
   System.out.println(var4);
}

public static final void main() {
   Object value$iv = "Learning Generics!";
   int $i$f$genericsExample = false;
   boolean var2 = false;
   System.out.println(value$iv);
   String var5 = "Type of T: " + String.class;
   boolean var3 = false;
   System.out.println(var5);
   Object value$iv = 100;
   $i$f$genericsExample = false;
   var2 = false;
   System.out.println(value$iv);
   var5 = "Type of T: " + Integer.class;
   var3 = false;
   System.out.println(var5);
}

由于我们在这里使用的是内联函数,因此该函数的代码将被复制并粘贴到我们将调用该函数的任何位置。此外,编译器正在将类型替换为实际类型,即 或。因此,无需显式传递类型。是不是很棒?是的,我知道,是的。我在博客的开头告诉过你????.TStringInt

注意:在Java中,我们不能使用reified函数,因为那里不支持内联函数。

除了上述功能外,还可以使用reified完成其他操作。例如,我们可以使用具有相同参数和名称但返回类型不同的函数。

具有不同返回类型的函数

考虑一种情况,您希望将函数的参数的名称、编号和类型保持为相同,但返回类型不同。我们可以通过函数重载来做到这一点吗?我看看。

例如,假设您要打印一些消息,如果学生的分数高于90,并且如果分数低于90,则只想返回标记。因此,我们可以想到两个同名的函数 。第一个函数将作为输入,并且仅在 小于 90 时调用,这将仅返回整数标记。同样,当高于 90 时将调用另一个函数,并将以字符串形式返回一些消息。showMessage marks marks marks

fun showMessage(marks: Int): Int {
    return marks
}
fun showMessage(marks: Int): String {
    return "Congratulations! you scored more than 90%";
}

上面的重载函数将引发错误,因为对于函数重载,参数的数量或参数类型应该不同,而不是返回类型。

因此,我们可以在此处使用关键字。修改后的代码将如下所示:reified

inline fun<reified T> showMessage(marks: Int): T {
    return when (T::class) {
        Int::class -> marks as T
        String::class -> "Congratulations! you scored more than 90%" as T
        else -> "Please enter valid type" as T
    }
}
fun main() {
    val intMarks: Int = showMessage(70) // returning integer value
    val stringMessage: String = showMessage(95) // returning string value
    println("Your marks: $intMarks \nMessage: $stringMessage")
}

上述代码的输出将是:

Your marks: 70 
Message: Congratulations! you scored more than 90%

在上面的示例中,您可以看到我们如何使用关键字从同一函数返回不同的数据类型(在我们的示例中为Int和String)。reified