You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin中获取实现泛型接口的类列表并保留类型可见性

解决方案:保留泛型类型信息加载Trick实现类

由于Kotlin/Java的类型擦除特性,运行时无法直接获取泛型接口的实际类型参数,这也是你用Auto-Service直接加载Trick只能得到List<Trick<*, *>>的原因。要保留精确的类型信息,需要让每个Trick实现类显式提供自身的类型元数据,结合Auto-Service完成服务发现。

步骤1:定义类型元数据载体与Provider接口

首先,创建用于存储Trick实例及其输入/输出类型的容器,再定义一个供Auto-Service扫描的Provider接口:

interface Trick<I, T> {
    fun perform(input: I): T
}

// 存储Trick实例与对应的类型信息
data class TrickHolder<I, T>(
    val trick: Trick<I, T>,
    val inputType: Class<I>,
    val outputType: Class<T>
)

// 供Auto-Service扫描的Provider接口,每个Trick实现对应一个Provider
interface TrickProvider {
    fun getTrickHolder(): TrickHolder<*, *>
}

步骤2:实现具体的Trick与对应的Provider

每个Trick实现类都要搭配一个标注了@AutoService的Provider,由Provider负责提供精确的类型信息:

// 示例Trick实现:字符串转整数
class StringToIntTrick : Trick<String, Int> {
    override fun perform(input: String): Int {
        return input.toInt()
    }
}

// 对应的Provider,用@AutoService标记让Auto-Service扫描到
@AutoService(TrickProvider::class)
class StringToIntTrickProvider : TrickProvider {
    override fun getTrickHolder(): TrickHolder<*, *> {
        return TrickHolder(
            trick = StringToIntTrick(),
            inputType = String::class.java,
            outputType = Int::class.java
        )
    }
}

步骤3:实现加载函数并使用

通过ServiceLoader加载所有TrickProvider,收集TrickHolder列表,之后可以根据类型信息匹配并使用对应的Trick:

// 加载所有TrickHolder
fun loadTricks(): List<TrickHolder<*, *>> {
    val serviceLoader = ServiceLoader.load(TrickProvider::class.java)
    return serviceLoader.map { it.getTrickHolder() }.toList()
}

// 根据输入类型匹配对应的Trick
fun <I, T> findTrickByInputType(input: I, tricks: List<TrickHolder<*, *>>): Trick<I, T>? {
    @Suppress("UNCHECKED_CAST")
    return tricks.firstOrNull { it.inputType.isInstance(input) }?.trick as? Trick<I, T>
}

// 使用示例
fun main() {
    val allTricks = loadTricks()
    val inputStr = "123"
    
    val stringToIntTrick = findTrickByInputType(inputStr, allTricks)
    stringToIntTrick?.perform(inputStr)?.let { println("转换结果:$it") } // 输出:转换结果:123
}

处理复杂泛型类型(如List)

如果你的Trick需要处理带泛型的输入/输出(比如List<String>),可以用TypeToken替代Class来保留完整的类型信息:

import com.google.common.reflect.TypeToken

data class TrickHolder<I, T>(
    val trick: Trick<I, T>,
    val inputType: TypeToken<I>,
    val outputType: TypeToken<T>
)

// 示例:List<String>转Set<Int>的Trick与Provider
class ListStringToSetIntTrick : Trick<List<String>, Set<Int>> {
    override fun perform(input: List<String>): Set<Int> {
        return input.map { it.toInt() }.toSet()
    }
}

@AutoService(TrickProvider::class)
class ListStringToSetIntTrickProvider : TrickProvider {
    override fun getTrickHolder(): TrickHolder<*, *> {
        val inputType = object : TypeToken<List<String>>() {}
        val outputType = object : TypeToken<Set<Int>>() {}
        return TrickHolder(ListStringToSetIntTrick(), inputType, outputType)
    }
}

核心思路总结

通过让每个Trick实现类显式提供类型元数据,绕过了类型擦除的限制;结合Auto-Service的服务发现机制,既避免了使用已停止维护的Reflections库,又能完整保留每个Trick的输入/输出类型信息,满足你的需求。

内容的提问来源于stack exchange,提问作者LeYAUable

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 22:20:33