如何使用泛型实现输入类型安全的Kotlin工厂模式?
最优解决方案核心思路
利用Kotlin的KClass运行时类型信息,提前让每个Provider声明自己支持的输入类型,工厂直接通过类型匹配找到对应Provider,完全规避不安全的类型转换异常,无需try-catch捕获。
分步代码改造
- 修改Provider接口,新增支持类型声明
interface ExampleFragmentProvider<T: ExampleInput> { // 声明当前Provider可处理的输入类型 val supportedType: KClass<T> suspend fun provide(pageType: T) : Fragment }
- 调整Provider实现类,补充supportedType配置
以HomepageProvider为例,其余Provider同理配置:
internal class ExampleHomepageProvider @Inject constructor() : ExampleFragmentProvider<ExampleInput.Homepage> { // 绑定当前Provider对应的输入类型 override val supportedType = ExampleInput.Homepage::class override suspend fun provide(pageType: ExampleInput.Homepage): Fragment { TODO() } }
- 重写工厂实现,通过类型匹配查找Provider
class ExampleFragmentFactoryImpl @Inject constructor( private val providers: List<ExampleFragmentProvider<out ExampleInput>> ): ExampleFragmentFactory { @Suppress("UNCHECKED_CAST") override suspend fun <T: ExampleInput> create( pageType: T ): Fragment { val targetType = pageType::class providers.forEach { provider -> if (provider.supportedType == targetType) { // 此处类型转换100%安全,已通过supportedType验证对应关系 return (provider as ExampleFragmentProvider<T>).provide(pageType) } } throw IllegalStateException("could not create Fragment for pageType=$pageType") } }
性能优化方案
如果Provider数量较多,可以在工厂初始化时直接构建类型到Provider的映射表,避免每次调用都遍历列表:
class ExampleFragmentFactoryImpl @Inject constructor( providers: List<ExampleFragmentProvider<out ExampleInput>> ): ExampleFragmentFactory { // 初始化时预构建映射表,查询复杂度从O(n)降为O(1) private val providerMapping = providers.associateBy { it.supportedType } @Suppress("UNCHECKED_CAST") override suspend fun <T: ExampleInput> create(pageType: T): Fragment { val provider = providerMapping[pageType::class] as? ExampleFragmentProvider<T> ?: throw IllegalStateException("could not create Fragment for pageType=$pageType") return provider.provide(pageType) } }
方案说明
- 所有类型校验都在编译+运行时双重保障,
@Suppress("UNCHECKED_CAST")是安全的,不会出现类型转换异常,无需try-catch包裹 - 符合开闭原则:新增输入类型仅需新增对应Provider实现,工厂侧无需修改任何代码
- 因为输入是
sealed class,可以保证所有可传入的类型都有对应的Provider覆盖,不会出现遗漏
内容的提问来源于stack exchange,提问作者ceedee
相关产品推荐
相关产品推荐

