内联函数中的Kotlin修饰类型

内联函数中的Kotlin修饰类型

I've noticed that many people haven't ever heard of reified types or have problems understanding what they are and what they do. Therefore this little post is intended to bring some light into the darkness of Kotlin's reified types.

起始情况

fun <T> myGenericFun(c: Class<T>)

In an ordinary generic function like myGenericFun, you can't access the type T because it is, like in Java, erased at runtime and thus only available at compile time. Therefore, if you want to use the generic type as a normal Class in the function body you need to explicitly pass the class as a parameter like the parameter c in the above example. That's correct and works fine but makes it a bit unsightly for the caller.

Inlined function with reified to the rescue

If, on the other hand, you use an inline function with a reified generic type T, the value of T can be accessed even at runtime and thus you don't need to pass the Class<T> additionally. You can work with T as if it was a normal Class, e.g. you might want to check whether a variable is an instance of T, which you can easily do like this: myVar is T.

An inline function with reified type looks like this:

inline fun <reified T> myGenericFun()

Be aware that reified types can only be used in combination with inline functions. Such an inline function makes the compiler copy the function's 通过 tecode to every place where the function is being called (the function is being "inlined"). When you call an inline function with reified type, the compiler knows the actual type used as a type argument and modifies the generated 通过 tecode to use the corresponding class directly. Therefore calls like myVar is T become myVar is String (if the type argument were String) in the 通过 tecode and at runtime.

实际行动

Let's have a look at an example, where reified is really helpful. We want to create an extension function for String called toKotlinObject, which tries to convert a JSON string to a Kotlin Object, specified 通过 the function's type T. We can use com.fasterxml.jackson.module.kotlin for this and the first approach is the following:

没有确定类型的第一种方法

fun <T> String.toKotlinObject(): T {
      val mapper = jacksonObjectMapper()
                                    //does not compile!
      return mapper.readValue(this, T::class.java)
}

The readValue method takes a type that it is supposed to parse the JsonObject to. If we try to get the Class of the type parameter T, the compiler complains: "Cannot use 'T' as reified type parameter. Use a class instead."

使用显式Class参数的解决方法

fun <T: Any> String.toKotlinObject(c: KClass<T>): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(this, c.java)
}

As a workaround, we pass the Class of T explicitly, which can directly be used as an argument to readValue. This works and is actually a common pattern in Java code for such scenarios. The function can be called like so:

data class MyJsonType(val name: String)

val json = """{"name":"example"}"""
json.toKotlinObject(MyJsonType::class)

Kotlin方式: reified

Using an inline function with reified type parameter T makes it possible to implement our function as follows:

inline fun <reified T: Any> String.toKotlinObject(): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(this, T::class.java)
}

There's no need to pass the Class of T additionally, T can be used as if it was an ordinary class. For the client the code looks like this:

json.toKotlinObject<MyJsonType>()

重要

Inline reified functions are not callable from Java code, whereas normal inline functions are. That's probably the reason why not all type parameters used in inline functions are reified 通过 default.

结论

This was just a quick introduction to reified types. In my opinion, the call to a function with reified types looks way better because we can make use of the <>-syntax commonly used whenever generics are relevant. As a result, it's more readable than the Java approach of passing a Class object as a parameter. More details can be read in this 规格文件.

如果您想了解有关Kotlin的美丽功能的更多信息,请推荐这本书 行动中的科特林 给你,也想带你去我的 其他文章 ðŸ™,

关于2个想法“内联函数中的Kotlin修饰类型

  • […与Java中相同,泛型类型仅在编译时可用,并在运行时删除。这对于大多数用例已经足够了,因为使用了泛型来确保类型安全。虽然有时候’能够在运行时检索实际信息也是很好的。幸运的是,Kotlin附带了一个称为reified types的功能,使得可以在运行时处理泛型类型(例如执行类型检查)。修饰类型的概念已在本文中进行了解释。 […]

发表评论

您的电子邮件地址不会被公开。 必需的地方已做标记 *