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
相关产品推荐
相关产品推荐

