feat: add TypeRef

This commit is contained in:
2025-12-13 21:35:10 +08:00
parent 0f1443b339
commit ac678ae6af
5 changed files with 151 additions and 1 deletions

View File

@@ -287,6 +287,38 @@ val myClass: Class<*>
val arguments = myClass.genericSuperclassTypeArguments()
```
### Type Reference Extensions
In Java, method generics are erased after compilation, and the type obtained at runtime is `java.lang.Object`.
KavaRef provides the `TypeRef` class to wrap your target generics to ensure that you can get the correct generic type at runtime.
Its core functionality is referenced from [Gson](https://github.com/google/gson)'s `TypeToken`.
It is very simple to use, you can use it like this.
> The following example
```kotlin
val listStringType = typeRef<List<String>>()
// Get the stored type, which will be List<? extends String>.
val type = listStringType.type
// Get its raw type, which will be List.
val rawType = listStringType.rawType
```
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.
> The following example
```kotlin
val gson = Gson()
inline fun <reified T : Any> T.toJson(): String = gson.toJson(this, typeRef<T>().type)
// Usage
val json = listOf("KavaRef", "is", "awesome").toJson()
```
### Java Wrapper Classes Extensions
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.

View File

@@ -268,6 +268,37 @@ val myClass: Class<*>
val arguments = myClass.genericSuperclassTypeArguments()
```
### 类型引用扩展
在 Java 中,方法的泛型会在编译后被类型擦除,在运行获取到的类型是 `java.lang.Object`
KavaRef 提供了 `TypeRef` 类来包装你的目标泛型来确保你可以在运行时获取到正确的泛型类型,它的核心功能参考于 [Gson](https://github.com/google/gson) 的 `TypeToken`
它的使用方法非常简单,你可以像下面这样使用它。
> 示例如下
```kotlin
val listStringType = typeRef<List<String>>()
// 获取存储的类型,将会是 List<? extends String>
val type = listStringType.type
// 获取其原始类型,将会是 List
val rawType = listStringType.rawType
```
在使用 Gson 等需要传入 `Type` 的场景中,你可以为此实现一个带有 `reified` 泛型的扩展方法。
> 示例如下
```kotlin
val gson = Gson()
inline fun <reified T : Any> T.toJson(): String = gson.toJson(this, typeRef<T>().type)
// 使用方法
val json = listOf("KavaRef", "is", "awesome").toJson()
```
### Java 包装类扩展
在 Kotlin 中直接使用 `Boolean::class``Byte::class` 等方式获取到的是 Java 的原始类型 `boolean``byte` 而不是它们的包装类。

View File

@@ -3,6 +3,7 @@ kotlin = "2.0.10"
dokka = "1.9.20"
maven-publish = "0.34.0"
slf4j = "2.0.17"
androidx-annotation = "1.9.1"
[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
@@ -12,3 +13,4 @@ maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "maven-publ
[libraries]
slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" }
slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "androidx-annotation" }

View File

@@ -24,3 +24,7 @@ kotlin {
)
}
}
dependencies {
implementation(libs.androidx.annotation)
}

View File

@@ -0,0 +1,81 @@
/*
* KavaRef - A modernizing Java Reflection with Kotlin.
* Copyright (C) 2019 HighCapable
* https://github.com/HighCapable/KavaRef
*
* Apache License Version 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is created by fankes on 2025/10/6.
*/
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
@file:JvmName("TypeRefUtils")
package com.highcapable.kavaref.extension
import androidx.annotation.Keep
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
/**
* Type reference class for getting generic parameter [T] type.
*
* The purpose of this class is to retain erased generics at runtime.
* @see typeRef
*/
@Keep
abstract class TypeRef<T : Any> {
/**
* Get the generic parameter [T] type.
* @return [Type]
*/
val type by lazy {
when (val superclass = javaClass.genericSuperclass) {
is ParameterizedType ->
if (superclass.rawType == classOf<TypeRef<*>>())
superclass.actualTypeArguments.firstOrNull() ?: error("Type argument cannot be null.")
else error("Must only create direct subclasses of TypeRef.")
classOf<TypeRef<*>>() -> error("TypeRef must be created with a type argument: object : TypeRef<...>() {}.")
else -> error("Must only create direct subclasses of TypeRef.")
}
}
/**
* Get the raw class type of the generic parameter [T].
* @return [Class]<[T]>
*/
val rawType by lazy { type.toClass<T>() }
override fun toString() = type.toString()
override fun equals(other: Any?) = other is TypeRef<*> && type == other.type
override fun hashCode() = type.hashCode()
}
/**
* Create a [TypeRef] instance with the reified type parameter [T].
*
* Usage:
*
* ```kotlin
* val typeRef = typeRef<List<String>>()
* // This will be of type `List<String>`.
* val type = typeRef.type
* // This will be of type `List`.
* val rawType = typeRef.rawType
* ```
* @see TypeRef
* @return [TypeRef]<[T]>
*/
inline fun <reified T : Any> typeRef() = object : TypeRef<T>() {}