一個(gè)依賴(lài)注入框架,無(wú)非就是注入與獲取實(shí)例。那么kodein是怎么注入的?
注入
val kodein = Kodein {
bind<Die>() with singleton { Die(instance()) }
bind<Random>() with singleton { Random(instance) }
constant(tag "max") with 5
}
可以看到首先要初始化Kodien
環(huán)境台猴,
KodeinImpl
internal open class KodeinImpl internal constructor(private val _container: KodeinContainerImpl) : Kodein {
@Suppress("unused")
private constructor(builder: KodeinMainBuilderImpl, runCallbacks: Boolean) : this(KodeinContainerImpl(builder.containerBuilder, builder.externalSources, builder.fullDescriptionOnError, runCallbacks))
constructor(allowSilentOverride: Boolean = false, init: Kodein.MainBuilder.() -> Unit) : this(newBuilder(allowSilentOverride, init), true)
companion object {
private fun newBuilder(allowSilentOverride: Boolean = false, init: Kodein.MainBuilder.() -> Unit) = KodeinMainBuilderImpl(allowSilentOverride).apply(init)
fun withDelayedCallbacks(allowSilentOverride: Boolean = false, init: Kodein.MainBuilder.() -> Unit): Pair<Kodein, () -> Unit> {
val kodein = KodeinImpl(newBuilder(allowSilentOverride, init), false)
return kodein to { kodein._container.initCallbacks?.invoke() ; Unit }
}
}
final override val container: KodeinContainer by lazy {
if (_container.initCallbacks != null)
throw IllegalStateException("Kodein has not been initialized")
_container
}
}
這里會(huì)內(nèi)部構(gòu)建好一個(gè)Kodien.Builder
,調(diào)用初始化函數(shù)后,傳入一個(gè)container
這里KodienBuilder
通過(guò)調(diào)用bind
返回一個(gè)binder
,供上層去綁定一個(gè)實(shí)例
Binders
所以我們看到builder
其實(shí)提供了一個(gè)bind
方法,對(duì)外暴露接口獲取binder卿吐。
inner class TypeBinder<T : Any> internal constructor(val type: TypeToken<out T>, val tag: Any?, val overrides: Boolean?) : Kodein.Builder.TypeBinder<T> {
internal val containerBuilder get() = this@KodeinBuilderImpl.containerBuilder
override infix fun <C, A> with(binding: KodeinBinding<in C, in A, out T>) = containerBuilder.bind(Kodein.Key(binding.contextType, binding.argType, type, tag), binding, moduleName, overrides)
}
inner class DirectBinder internal constructor(private val _tag: Any?, private val _overrides: Boolean?) : Kodein.Builder.DirectBinder {
override infix fun <C, A, T: Any> from(binding: KodeinBinding<in C, in A, out T>) {
if (binding.createdType == UnitToken) {
throw IllegalArgumentException("Using `bind() from` with a *Unit* ${binding.factoryName()} is most likely an error. If you are sure you want to bind the Unit type, please use `bind<Unit>() with ${binding.factoryName()}`.")
}
containerBuilder.bind(Kodein.Key(binding.contextType, binding.argType, binding.createdType, _tag), binding, moduleName, _overrides)
}
}
inner class ConstantBinder internal constructor(private val _tag: Any, private val _overrides: Boolean?) : Kodein.Builder.ConstantBinder {
@Suppress("FunctionName")
override fun <T: Any> With(valueType: TypeToken<out T>, value: T) = Bind(tag = _tag, overrides = _overrides) from InstanceBinding(valueType, value)
}
然后我們來(lái)看看binder
可以綁定哪些binding
Bindings
/**
* @param C The type of the context used by the retriever.
* @param A The type of argument used to create or retrieve an instance.
* @param T The type of instance this factory creates or retrieves.
*/
interface Binding<C, A, T: Any> {
fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, A, T>): (A) -> T
}
Binding
是什么旁舰,就是根據(jù)key
返回一個(gè)構(gòu)造器,這個(gè)構(gòu)造器接受一個(gè)參數(shù)返回一個(gè)實(shí)例嗡官。
Factory
class Factory<C, A, T: Any>(override val contextType: TypeToken<in C>, override val argType: TypeToken<in A>, override val createdType: TypeToken<out T>, private val creator: BindingKodein<C>.(A) -> T) : KodeinBinding<C, A, T> {
override fun factoryName() = "factory"
override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, A, T>): (A) -> T = { arg -> this.creator(kodein, arg) }
}
Multiton
class Multiton<C, A, T: Any>(override val scope: Scope<C>, override val contextType: TypeToken<in C>, override val argType: TypeToken<in A>, override val createdType: TypeToken<out T>, refMaker: RefMaker? = null, val sync: Boolean = true, private val creator: SimpleBindingKodein<C>.(A) -> T) : KodeinBinding<C, A, T> {
private val _refMaker = refMaker ?: SingletonReference
private val _scopeId = Any()
override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, A, T>): (A) -> T {
val registry = scope.getRegistry(kodein.context)
return { arg ->
@Suppress("UNCHECKED_CAST")
registry.getOrCreate(ScopeKey(_scopeId, arg), sync) { _refMaker.make { BindingContextedKodein(kodein, kodein.context).creator(arg) } } as T
}
}
override val copier = KodeinBinding.Copier { Multiton(scope, contextType, argType, createdType, _refMaker, sync, creator) }
}
首先通過(guò)scope.getRegistry(kodein.context)
拿到registry
,啥意思箭窜,拿到一個(gè)存儲(chǔ)實(shí)例的registry
,這里可以和生命周期(context)綁定。
registry.getOrCreate(ScopeKey(_scopeId, arg), sync) { _refMaker.make { BindingContextedKodein(kodein, kodein.context).creator(arg) } } as T
對(duì)于scopeKey
(是data class)衍腥,可以看到如果arg
一樣是不會(huì)create的磺樱,如果不存在的話,通過(guò)creator()
函數(shù)創(chuàng)建一個(gè)
Provider
class Provider<C, T: Any>(override val contextType: TypeToken<in C>, override val createdType: TypeToken<out T>, val creator: NoArgBindingKodein<C>.() -> T) : NoArgKodeinBinding<C, T> {
override fun factoryName() = "provider"
override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, Unit, T>): (Unit) -> T = { NoArgBindingKodeinWrap(kodein).creator() }
}
Singleton
class Singleton<C, T: Any>(override val scope: Scope<C>, override val contextType: TypeToken<in C>, override val createdType: TypeToken<out T>, refMaker: RefMaker? = null, val sync: Boolean = true, val creator: NoArgSimpleBindingKodein<C>.() -> T) : NoArgKodeinBinding<C, T> {
@Suppress("UNCHECKED_CAST")
private val _refMaker = refMaker ?: SingletonReference
private val _scopeKey = ScopeKey(Any(), Unit)
/**
* @see [KodeinBinding.getFactory]
*/
override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, Unit, T>): (Unit) -> T {
val registry = scope.getRegistry(kodein.context)
return {
@Suppress("UNCHECKED_CAST")
registry.getOrCreate(_scopeKey, sync) { _refMaker.make { NoArgBindingKodeinWrap(BindingContextedKodein(kodein, kodein.context)).creator() } } as T
}
}
override val copier = KodeinBinding.Copier { Singleton(scope, contextType, createdType, _refMaker, sync, creator) }
}
InstanceBinding
class InstanceBinding<T: Any>(override val createdType: TypeToken<out T>, val instance: T) : NoArgKodeinBinding<Any?, T> {
override fun factoryName() = "instance"
override val contextType = AnyToken
/**
* @see [KodeinBinding.getFactory]
*/
override fun getFactory(kodein: BindingKodein<Any?>, key: Kodein.Key<Any?, Unit, T>): (Unit) -> T = { this.instance }
}
就返回一個(gè)已經(jīng)初始化好的instance
Eager
class EagerSingleton<T: Any>(builder: KodeinContainer.Builder, override val createdType: TypeToken<out T>, val creator: NoArgSimpleBindingKodein<Any?>.() -> T) : NoArgKodeinBinding<Any?, T> {
override val contextType = AnyToken
@Volatile private var _instance: T? = null
private val _lock = Any()
private fun getFactory(kodein: BindingKodein<Any?>): (Unit) -> T {
return { _ ->
synchronizedIfNull(
lock = _lock,
predicate = this@EagerSingleton::_instance,
ifNotNull = { it },
ifNull = {
NoArgBindingKodeinWrap(kodein).creator().also { _instance = it }
}
)
}
}
init {
val key = Kodein.Key(AnyToken, UnitToken, createdType, null)
builder.onReady { getFactory(BindingKodeinImpl(this, key, null, 0)).invoke(Unit) }
}
override val copier = KodeinBinding.Copier { builder -> EagerSingleton(builder, createdType, creator) }
}
builder.onReady
,當(dāng)kodein容器??
準(zhǔn)備好時(shí)就通過(guò)getFactory
初始化實(shí)例
這里我們暫時(shí)知道bindings
就是返回一個(gè)函數(shù)婆咸,這個(gè)函數(shù)傳入?yún)?shù)能返回我們需要的實(shí)例T竹捉。
那么binder
和binding
是怎么結(jié)合起來(lái)的,就通過(guò)一個(gè)初始化好的KodeinContainerBuilder
去對(duì)bindings
進(jìn)行配置尚骄。
KodeinContainerBuilderImpl
配置bindings
internal class KodeinContainerBuilderImpl(
allowOverride: Boolean,
silentOverride: Boolean,
internal val bindingsMap: MutableMap<Kodein.Key<*, *, *>, MutableList<KodeinDefining<*, *, *>>>,
internal val callbacks: MutableList<DKodein.() -> Unit>,
internal val translators: MutableList<ContextTranslator<*, *>>
)
配置能否override
,持有bindingMap
,
Bind
當(dāng)開(kāi)始綁定時(shí),會(huì)將綁定關(guān)系放入bindingMap
val bindings = bindingsMap.getOrPut(key) { newLinkedList() }
bindings.add(0, KodeinDefining(binding, fromModule))
Extend
配置可以從已有的kodeinContainer
中繼承
override fun extend(container: KodeinContainer, allowOverride: Boolean, copy: Set<Kodein.Key<*, *, *>>) {
checkMatch(allowOverride)
container.tree.bindings.forEach { (key, bindings) ->
if (!allowOverride)
checkOverrides(key, null)
val newBindings = if (key in copy) {
newLinkedList<KodeinDefining<*, *, *>>().also {
bindings.mapTo(it) { KodeinDefining(it.binding.copier?.copy(this@KodeinContainerBuilderImpl) ?: it.binding, it.fromModule) }
}
}
else {
newLinkedList<KodeinDefining<*, *, *>>(bindings)
}
bindingsMap[key] = newBindings
}
translators += container.tree.registeredTranslators
}
獲取實(shí)例
前面bb了這么多块差,那怎么獲取實(shí)例呢?
private val viewModel: AlbumListViewModel by instance()
fun <T : Any> KodeinAware.Instance(type: TypeToken<out T>, tag: Any? = null): KodeinProperty<T> =
KodeinProperty(kodeinTrigger, kodeinContext) { ctx, _ -> kodein.container.provider(Kodein.Key(ctx.anyType, UnitToken, type, tag), ctx.value).invoke() }
這里返回一個(gè)lazy
屬性倔丈,
KodeinProperty
kodein
使用lazy
能力憨闰。
通過(guò)定義 provideDelegate 操作符,可以擴(kuò)展創(chuàng)建屬性實(shí)現(xiàn)所委托對(duì)象的邏輯:
解釋
interface LazyDelegate<out V> {
/** @suppress */
operator fun provideDelegate(receiver: Any?, prop: KProperty<Any?>): Lazy<V>
}
class KodeinProperty<out V>(internal val trigger: KodeinTrigger?, val originalContext: KodeinContext<*>, private val get: (KodeinContext<*>, String) -> V) : LazyDelegate<V> {
override fun provideDelegate(receiver: Any?, prop: KProperty<Any?>): Lazy<V> = lazy {
@Suppress("UNCHECKED_CAST")
val context = if (receiver != null && originalContext === AnyKodeinContext) KodeinContext(TTOf(receiver) as TypeToken<in Any>, receiver) else originalContext
get(context, prop.name) } .also { trigger?.properties?.add(it)
}
}
class KodeinPropertyMap<in I, out O>(private val base: KodeinProperty<I>, private val map: (I) -> O) : LazyDelegate<O> {
override fun provideDelegate(receiver: Any?, prop: KProperty<Any?>): Lazy<O> = lazy { map(base.provideDelegate(receiver, prop).value) }.also { base.trigger?.properties?.add(it) }
}
翻譯一下:
假設(shè)一個(gè)var prop: Type by KodeinProperty()
=>
class C {
// calling "provideDelegate" to create the additional "delegate" property
private val prop$delegate = KodeinProperty().provideDelegate(this, this::prop)
val prop: Type
get() = prop$delegate.getValue(this, this::prop)
}
這里返回了lazy
,即當(dāng)這個(gè)屬性被用到時(shí)需五,會(huì)調(diào)用get
方法
那我們?cè)賮?lái)看看這段代碼:
fun <A, T : Any> KodeinAware.Factory(argType: TypeToken<in A>, type: TypeToken<out T>, tag: Any? = null): KodeinProperty<(A) -> T> =
KodeinProperty(kodeinTrigger, kodeinContext) { ctx, _ -> kodein.container.factory(Kodein.Key(ctx.anyType, argType, type, tag), ctx.value) }
看源碼可以看到Factory&Instance
的不同鹉动,前者lazy
返回一個(gè)N階函數(shù),后者lazy
直接返回一個(gè)實(shí)例宏邮。
所以很簡(jiǎn)單泽示,就是通過(guò)kodein.container.factory
去獲取。
KodeinContainerImpl
獲取factory
,什么是工廠蜜氨,就是別人傳入一個(gè)A(arg)
返回自己需要的實(shí)例械筛。
tree.find(key)
首先在樹(shù)中根據(jù)鍵值尋找,kodein
在綁定關(guān)系的時(shí)候初始化了一個(gè)containerimpl
, 初始化了一顆樹(shù)记劝,
tree.find(key, 0).let {
if (it.size == 1) {
val (_, definition, translator) = it[0]
node?.check(key, 0)
val kContext = translator?.toKContext(context) ?: KodeinContext(key.contextType, context) as KodeinContext<Any>
key as Kodein.Key<Any, A, T>
val bindingKodein = bindingKodein(key, kContext, definition.tree, overrideLevel)
return definition.binding.getFactory(bindingKodein, key)
}
}
如果找到一個(gè)(有且一個(gè))变姨,拿到definition
,即binding
的定義族扰。檢查是否有循環(huán)依賴(lài)
bindingKodein
val bindingKodein = bindingKodein(key, kContext, definition.tree, overrideLevel)
return definition.binding.getFactory(bindingKodein, key)
這里會(huì)創(chuàng)建一個(gè)bindingKodein
的東西厌丑,在bindings
去getFactory
,為什么要傳入這么一個(gè)東西呢渔呵?
NoArgBindingKodeinWrap(BindingContextedKodein(kodein, kodein.context)).creator()
我們知道在實(shí)例化一個(gè)類(lèi)時(shí)怒竿,這個(gè)類(lèi)可能還會(huì)依賴(lài)其他類(lèi),綁定可能如下:
bind<AlbumDetailViewModel>() with scoped<Fragment>(AndroidLifecycleScope).singleton {
KotlinViewModelProvider.of(context) { AlbumDetailViewModel(instance(), instance()) }
}
在里面還需要instance()
實(shí)例化扩氢,那為什么不能直接用kodein
的環(huán)境呢耕驰,因?yàn)榘垂倬W(wǎng)說(shuō)法,還可以進(jìn)行override:
val testModule = Kodein.Module(name = "test") {
bind<Logger>(overrides = true) with singleton { FileLoggerWrapper("path/to/file", overriddenInstance()) }
}
所以其實(shí)bindingKodein
也是kodein
環(huán)境:
internal open class BindingKodeinImpl<out C, out A, out T: Any> internal constructor(
override val dkodein: DKodein,
private val _key: Kodein.Key<C, A, T>,
override val context: C,
private val _overrideLevel: Int
) : DKodein by dkodein, BindingKodein<C> {
override fun overriddenFactory(): (Any?) -> Any = container.factory(_key, context, _overrideLevel + 1) as (Any?) -> Any
override fun overriddenFactoryOrNull(): ((Any?) -> Any)? = container.factoryOrNull(_key, context, _overrideLevel + 1) as ((Any?) -> Any)?
}
它包括什么录豺,它包括DKodien
, Dkodien
又是一個(gè)新的概念朦肘,
If you don’t want to use delegated properties, Kodein has you covered. Most of the features available to Kodein are available to DKodein (D is for Direct). DKodein allows you to directly get a new instance or dependency
它就比kodein
少了delegate
屬性饭弓,因此也沒(méi)有receiver & lazy
的概念。
override fun overriddenFactory(): (Any?) -> Any = container.factory(_key, context, _overrideLevel + 1) as (Any?) -> Any
正常綁定時(shí)使用的是container.factory(0)
,這里會(huì)使用+1
表明使用override
的
循環(huán)依賴(lài) node.check
val kodein = Kodein {
bind<Die>() with singleton { Die(instance()) }
bind<Random>() with singleton { Random(instance) }
constant(tag "max") with 5
}
org.kodein.di.Kodein$DependencyLoopException: Dependency recursion:
bind<View>()
╔╩>bind<Presenter>()
║ ╚>bind<View>()
╚════╝
回顧一下媒抠,
弟断,當(dāng)我們調(diào)用by instance
時(shí)會(huì)發(fā)生什么,會(huì)返回一個(gè)函數(shù)趴生,函數(shù)執(zhí)行時(shí)會(huì)調(diào)用
kodein.container.provider(Kodein.Key(ctx.anyType, UnitToken, type, tag), ctx.value).invoke()
,即調(diào)用creator
,即Die(instance())
,這里調(diào)用instance()
時(shí),背后調(diào)用DKodeinBaseImpl.container.provider.invoke()
,那這里的container是誰(shuí)呢阀趴,
先看DKodeinBaseImpl
是誰(shuí),就是調(diào)用by instance
的container
,所以這里container
都是同一個(gè),很正常能看出循環(huán)依賴(lài)苍匆。
我們看刘急,在找factory時(shí)
,
private tailrec fun recursiveCheck(node: Node, searchedKey: Kodein.Key<*, *, *>, searchedOverrideLevel: Int): Boolean {
return if (node.key == searchedKey && node.overrideLevel == searchedOverrideLevel)
false
else if (node.parent == null)
true
else
recursiveCheck(node.parent, searchedKey, searchedOverrideLevel)
}
開(kāi)始Node
是Die
, 然后Node
是Random
,然后 searchKey
是die
, 因此在遞歸上去時(shí)node -> parent
又是die
,導(dǎo)致循環(huán)依賴(lài)浸踩。
KodeinTreeImpl
一個(gè)緩存:
private val _cache: MutableMap<Kodein.Key<*, *, *>, Triple<Kodein.Key<*, *, *>, List<KodeinDefinition<*, *, *>>, ContextTranslator<*, *>?>>
private typealias BoundTypeTree = MutableMap<TypeChecker, ContextTypeTree>
private typealias ContextTypeTree = MutableMap<TypeChecker.Down, ArgumentTypeTree>
private typealias ArgumentTypeTree = MutableMap<TypeChecker.Down, TagTree>
private typealias TagTree = MutableMap<Any?, Kodein.Key<*, *, *>>
其中因?yàn)?code>kodien可以override
,所以會(huì)有definition lists
:
val kodein = Kodein {
bind<API>() with singleton { APIImpl() }
/* ... */
bind<API>(overrides = true) with singleton { OtherAPIImpl() }
}
尋找過(guò)程
-
_cache[key]
先從key
中尋找 -
_cache[anyContextKey]
尋找anyContextKey
當(dāng)然translator.contextType = key.contextType
Inject or Retrieve
inject
class Controller(private val ds: DataSource) {
/*...*/
}
val controller by kodein.newInstance { Controller(instance()) }
這里會(huì)走到Controller(instance())
, 在instance
里會(huì)自己去取實(shí)例叔汁。
retrive
class Controller(override val kodein: Kodein): KodeinAware {
private val ds: DataSource by instance()
}
這兩種方式,一種是自己就是一個(gè)kodein = KodeinAware
內(nèi)部會(huì)去取實(shí)例检碗,另一種是自己由外部注入攻柠。
SubTypes
val kodein = Kodein {
bind<Controller>().subtypes() with { type ->
when (type.jvmType) {
is MySpecialController::class.java -> singleton { MySpecialController() }
else -> provider { myControllerSystem.getController(type.jvmType) }
}
}
}
看到這里問(wèn)題,我綁定的是T后裸,這個(gè)T的子類(lèi)也會(huì)找到:
data class Down(override val type: TypeToken<*>) : TypeChecker() {
val isAny = (type == AnyToken)
override fun check(other: TypeToken<*>) = isAny || type.isAssignableFrom(other)
}
Contexted & Scoped
在kodien
中瑰钮,所有binder with binding
都會(huì)帶一個(gè)scope&context
:對(duì)于默認(rèn)的是AnyToken&NoScope
override val contextType = AnyToken
override val scope: Scope<Any?> get() = NoScope() // Recreating a new NoScope every-time *on purpose*!
那么,假設(shè)我們想要綁到一個(gè)具體的context&scope
時(shí)微驶,可以按如下這么寫(xiě):
val kodein = Kodein {
bind<Writer>() with contexted<Request>.provider { context.response.writer }
}
val kodein = Kodein {
bind<Session>() with scoped(requestScope).singleton { context.openSession() }
}
val session: Session by kodein.on(context = request).instance()
Scopes are derived from the context variable.
這里context<Request> & scoped(requestScope)
會(huì)返回一個(gè)新的builder
,綁定新的context&scope
, 當(dāng)使用kodein.on
時(shí)會(huì)創(chuàng)建一個(gè)新的kodein
,kodeinContext
是傳入的request
,我們?cè)賮?lái)重看一下getFactory
:
override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, A, T>): (A) -> T {
val registry = scope.getRegistry(kodein.context)
return { arg ->
@Suppress("UNCHECKED_CAST")
registry.getOrCreate(ScopeKey(_scopeId, arg), sync) { _refMaker.make { BindingContextedKodein(kodein, kodein.context).creator(arg) } } as T
}
}
我們舉個(gè)scope的例子:
open class AndroidLifecycleScope private constructor(private val newRegistry: () -> ScopeRegistry) : Scope<LifecycleOwner> {
companion object multiItem: AndroidLifecycleScope(::StandardScopeRegistry)
object singleItem: AndroidLifecycleScope(::SingleItemScopeRegistry)
private val map = HashMap<LifecycleOwner, ScopeRegistry>()
override fun getRegistry(context: LifecycleOwner): ScopeRegistry {
return synchronizedIfNull(
lock = map,
predicate = { map[context] },
ifNotNull = { it },
ifNull = {
val registry = newRegistry()
map[context] = registry
context.lifecycle.addObserver(object : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
context.lifecycle.removeObserver(this)
registry.clear()
map.remove(context)
}
})
registry
}
)
}
}
TypedReference
- TypeVariable(類(lèi)型變量):比如List<T>中的T等
- WildcardType( 泛型表達(dá)式類(lèi)型):例如List< ? extends Number>這種
- ParameterizedType(參數(shù)化類(lèi)型):就是我們平常所用到的泛型List浪谴、Map(注意和TypeVariable的區(qū)別)
- GenericArrayType(數(shù)組類(lèi)型):并不是我們工作中所使用的數(shù)組String[] 、byte[](這種都屬于Class)因苹,而是帶有泛型的數(shù)組苟耻,即T[] 泛型數(shù)組
private fun Type._checkIsReified(disp: Any) {
when (val jvmType = javaType) {
is Class<*> -> {}
is ParameterizedType -> for (arg in jvmType.actualTypeArguments) arg._checkIsReified(disp)
is GenericArrayType -> jvmType.genericComponentType._checkIsReified(disp)
is WildcardType -> {
for (arg in jvmType.lowerBounds)
arg._checkIsReified(disp)
for (arg in jvmType.upperBounds)
arg._checkIsReified(disp)
}
is TypeVariable<*> -> throw IllegalArgumentException("$disp uses a type variable named ${jvmType.name}, therefore, the bound value can never be retrieved.")
else -> throw IllegalArgumentException("Unknown type ${jvmType.javaClass} $jvmType")
}
}
反正就是一定要具體化泛型,不要有<T>這種