Skip to content

Commit ac678ae

Browse files
committed
feat: add TypeRef
1 parent 0f1443b commit ac678ae

File tree

5 files changed

+151
-1
lines changed

5 files changed

+151
-1
lines changed

docs-source/src/en/library/kavaref-extension.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,38 @@ val myClass: Class<*>
287287
val arguments = myClass.genericSuperclassTypeArguments()
288288
```
289289

290+
### Type Reference Extensions
291+
292+
In Java, method generics are erased after compilation, and the type obtained at runtime is `java.lang.Object`.
293+
294+
KavaRef provides the `TypeRef` class to wrap your target generics to ensure that you can get the correct generic type at runtime.
295+
Its core functionality is referenced from [Gson](https://github.com/google/gson)'s `TypeToken`.
296+
297+
It is very simple to use, you can use it like this.
298+
299+
> The following example
300+
301+
```kotlin
302+
val listStringType = typeRef<List<String>>()
303+
// Get the stored type, which will be List<? extends String>.
304+
val type = listStringType.type
305+
// Get its raw type, which will be List.
306+
val rawType = listStringType.rawType
307+
```
308+
309+
In scenarios where you need to pass in `Type` such as when using Gson, you can implement an extension method with `reified` generics for this purpose.
310+
311+
> The following example
312+
313+
```kotlin
314+
val gson = Gson()
315+
316+
inline fun <reified T : Any> T.toJson(): String = gson.toJson(this, typeRef<T>().type)
317+
318+
// Usage
319+
val json = listOf("KavaRef", "is", "awesome").toJson()
320+
```
321+
290322
### Java Wrapper Classes Extensions
291323

292324
In Kotlin, you can directly use `Boolean::class`, `Byte::class`, etc. to obtain Java's original types `boolean` and `byte` instead of their wrapper classes.

docs-source/src/zh-cn/library/kavaref-extension.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,37 @@ val myClass: Class<*>
268268
val arguments = myClass.genericSuperclassTypeArguments()
269269
```
270270

271+
### 类型引用扩展
272+
273+
在 Java 中,方法的泛型会在编译后被类型擦除,在运行获取到的类型是 `java.lang.Object`
274+
275+
KavaRef 提供了 `TypeRef` 类来包装你的目标泛型来确保你可以在运行时获取到正确的泛型类型,它的核心功能参考于 [Gson](https://github.com/google/gson)`TypeToken`
276+
277+
它的使用方法非常简单,你可以像下面这样使用它。
278+
279+
> 示例如下
280+
281+
```kotlin
282+
val listStringType = typeRef<List<String>>()
283+
// 获取存储的类型,将会是 List<? extends String>
284+
val type = listStringType.type
285+
// 获取其原始类型,将会是 List
286+
val rawType = listStringType.rawType
287+
```
288+
289+
在使用 Gson 等需要传入 `Type` 的场景中,你可以为此实现一个带有 `reified` 泛型的扩展方法。
290+
291+
> 示例如下
292+
293+
```kotlin
294+
val gson = Gson()
295+
296+
inline fun <reified T : Any> T.toJson(): String = gson.toJson(this, typeRef<T>().type)
297+
298+
// 使用方法
299+
val json = listOf("KavaRef", "is", "awesome").toJson()
300+
```
301+
271302
### Java 包装类扩展
272303

273304
在 Kotlin 中直接使用 `Boolean::class``Byte::class` 等方式获取到的是 Java 的原始类型 `boolean``byte` 而不是它们的包装类。

gradle/libs.versions.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ kotlin = "2.0.10"
33
dokka = "1.9.20"
44
maven-publish = "0.34.0"
55
slf4j = "2.0.17"
6+
androidx-annotation = "1.9.1"
67

78
[plugins]
89
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
@@ -11,4 +12,5 @@ maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "maven-publ
1112

1213
[libraries]
1314
slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" }
14-
slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
15+
slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
16+
androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "androidx-annotation" }

kavaref-extension/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,8 @@ kotlin {
2323
"-Xno-receiver-assertions"
2424
)
2525
}
26+
}
27+
28+
dependencies {
29+
implementation(libs.androidx.annotation)
2630
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* KavaRef - A modernizing Java Reflection with Kotlin.
3+
* Copyright (C) 2019 HighCapable
4+
* https://github.com/HighCapable/KavaRef
5+
*
6+
* Apache License Version 2.0
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* https://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*
20+
* This file is created by fankes on 2025/10/6.
21+
*/
22+
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
23+
@file:JvmName("TypeRefUtils")
24+
25+
package com.highcapable.kavaref.extension
26+
27+
import androidx.annotation.Keep
28+
import java.lang.reflect.ParameterizedType
29+
import java.lang.reflect.Type
30+
31+
/**
32+
* Type reference class for getting generic parameter [T] type.
33+
*
34+
* The purpose of this class is to retain erased generics at runtime.
35+
* @see typeRef
36+
*/
37+
@Keep
38+
abstract class TypeRef<T : Any> {
39+
40+
/**
41+
* Get the generic parameter [T] type.
42+
* @return [Type]
43+
*/
44+
val type by lazy {
45+
when (val superclass = javaClass.genericSuperclass) {
46+
is ParameterizedType ->
47+
if (superclass.rawType == classOf<TypeRef<*>>())
48+
superclass.actualTypeArguments.firstOrNull() ?: error("Type argument cannot be null.")
49+
else error("Must only create direct subclasses of TypeRef.")
50+
classOf<TypeRef<*>>() -> error("TypeRef must be created with a type argument: object : TypeRef<...>() {}.")
51+
else -> error("Must only create direct subclasses of TypeRef.")
52+
}
53+
}
54+
55+
/**
56+
* Get the raw class type of the generic parameter [T].
57+
* @return [Class]<[T]>
58+
*/
59+
val rawType by lazy { type.toClass<T>() }
60+
61+
override fun toString() = type.toString()
62+
override fun equals(other: Any?) = other is TypeRef<*> && type == other.type
63+
override fun hashCode() = type.hashCode()
64+
}
65+
66+
/**
67+
* Create a [TypeRef] instance with the reified type parameter [T].
68+
*
69+
* Usage:
70+
*
71+
* ```kotlin
72+
* val typeRef = typeRef<List<String>>()
73+
* // This will be of type `List<String>`.
74+
* val type = typeRef.type
75+
* // This will be of type `List`.
76+
* val rawType = typeRef.rawType
77+
* ```
78+
* @see TypeRef
79+
* @return [TypeRef]<[T]>
80+
*/
81+
inline fun <reified T : Any> typeRef() = object : TypeRef<T>() {}

0 commit comments

Comments
 (0)