main.reflection.TypeRef.kt Maven / Gradle / Ivy
package de.peekandpoke.ultra.common.reflection
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.KTypeProjection
import kotlin.reflect.full.createType
/**
* Creates a type safe representation of the given [KType]
*
* [TypeRef] is a wrapper around KType. It also carries type information in [T].
*
* By doing so we can utilize the compiler for checking that reflection types are used correctly.
*/
data class TypeRef internal constructor(val type: KType) {
companion object {
/** Cache for [KType] to [TypeRef] */
private val cachedKTypes = mutableMapOf>()
/** Cache for [KClass] to [TypeRef] */
private val cachedNullableKClasses = mutableMapOf, TypeRef<*>>()
/** Cache for [KClass] to [TypeRef] */
private val cachedNonNullKClasses = mutableMapOf, TypeRef<*>>()
fun createForKType(type: KType): TypeRef {
@Suppress("UNCHECKED_CAST")
return cachedKTypes.getOrPut(type) {
TypeRef(type)
} as TypeRef
}
fun createForKClass(cls: KClass<*>, nullable: Boolean): TypeRef {
val cache = if (nullable) {
cachedNullableKClasses
} else {
cachedNonNullKClasses
}
@Suppress("UNCHECKED_CAST")
return cache.getOrPut(cls) {
val type = cls.createType(
arguments = cls.typeParameters.map {
KTypeProjection.invariant(kotlin.Any::class.createType())
}
)
// Return
createForKType(type)
} as TypeRef
}
val Unit = kType()
val UnitNull = kType()
val Any = kType()
val AnyNull = kType()
val Boolean = kType()
val BooleanNull = kType()
val Byte = kType()
val ByteNull = kType()
val Char = kType()
val CharNull = kType()
val Double = kType()
val DoubleNull = kType()
val Float = kType()
val FloatNull = kType()
val Int = kType()
val IntNull = kType()
val Long = kType()
val LongNull = kType()
val Number = kType()
val NumberNull = kType()
val Short = kType()
val ShortNull = kType()
val String = kType()
val StringNull = kType()
}
/**
* Returns a [ReifiedKType] of this TypeRef
*/
val reified: ReifiedKType by lazy { ReifiedKType(type) }
/**
* Converts to a nullable type
*/
val nullable: TypeRef by lazy(LazyThreadSafetyMode.NONE) {
@Suppress("RemoveExplicitTypeArguments")
TypeRef(
type.classifier!!.createType(
arguments = type.arguments,
nullable = true
)
)
}
/**
* Wraps the current type as a [List] type
*/
val list: TypeRef> by lazy(LazyThreadSafetyMode.NONE) {
@Suppress("RemoveExplicitTypeArguments")
createForKType>(
List::class.createType(
arguments = listOf(KTypeProjection.invariant(type)),
nullable = false
)
)
}
/**
* Wraps the current type with the given generic type [W]
*
* E.g. makes a String? type a MyWrapper type
*
* When the given type [W] does not have exactly one type parameter an exception is thrown
*/
inline fun wrapWith(): TypeRef {
@Suppress("USELESS_IS_CHECK")
return wrapWith(cls = W::class, null is W)
}
/**
* Wraps the current type with the given generic type [W]
*
* E.g. makes a String? type a MyWrapper type
*
* When the given type [W] does not have exactly one type parameter an exception is thrown
*/
@Suppress("UNCHECKED_CAST")
fun wrapWith(cls: KClass, nullable: Boolean): TypeRef {
if (cls.typeParameters.size != 1) {
error("Can only wrap with a generic type with exactly one type parameter")
}
val cache = if (nullable) {
nullableWrapCache
} else {
nonNullWrapCache
}
return cache.getOrPut(cls) {
createForKType(
cls.createType(
arguments = listOf(KTypeProjection.invariant(type)),
nullable = nullable
)
)
} as TypeRef
}
/**
* Internal helper val, that does not carry the correct generic type information.
*
* @see [unList] which takes care of the generic type information
*/
internal val unlisted: TypeRef by lazy(LazyThreadSafetyMode.NONE) {
if (type.classifier != List::class) {
error("The current type [$this] must have the classifier List::class")
}
@Suppress("RemoveExplicitTypeArguments")
TypeRef(type.arguments[0].type!!)
}
/**
* Internal cache used by [wrapWith]
*/
private val nonNullWrapCache = mutableMapOf, TypeRef<*>>()
/**
* Internal cache used by [wrapWith]
*/
private val nullableWrapCache = mutableMapOf, TypeRef<*>>()
}
/**
* Unwraps a List-type
*
* E.g. makes a List-type a String?-type.
*
* When the classifier of the current type is not List::class an exception is thrown.
*/
@Suppress("UNCHECKED_CAST")
val TypeRef>.unList: TypeRef
get() = unlisted as TypeRef
© 2015 - 2024 Weber Informatics LLC | Privacy Policy