Kotlin泛型适配条件类型:统一异构获取存储操作遇阻
问题分析
你的核心问题在于:if-else分支中fetcher和storer的类型无法被编译器统一推断,且fetcher返回的Either<Throwable, X>类型与storer需要的原始X类型不匹配,导致泛型复用失败。reified关键字在这里无法解决问题——它用于运行时获取泛型类型信息,但你的问题本质是编译时类型不兼容。
解决方案
1. 先修正核心类型不匹配问题
fetcher返回的是包裹在Either中的数据,但storer需要的是原始数据类型,所以必须先解包Either。假设你用trust()方法处理Either(成功返回值,失败抛出异常),先修改fetchAndStore:
private suspend inline fun <X, R : Comparable<R>> fetchAndStore( stock: Stock, periodicity: StockDataPeriodicity, fetcher: suspend (symbol: String, periodicity: StockDataPeriodicity) -> Either<Throwable, X>, storer: (stock: Stock, values: X) -> Either<Throwable, List<Entity<R>>> ) { // 先解包Either获取原始数据 val fetchedValues = fetcher(stockDetails.symbol, periodicity).trust() storer(stock, fetchedValues) }
2. 解决分支类型统一问题
有两种可靠的方式实现逻辑复用:
方式一:将泛型调用拆分到分支中
直接在if-else分支内调用fetchAndStore,让编译器在每个分支中独立推断类型,避免强制统一类型:
suspend fun startFetching(dataPeriodicity: StockDataPeriodicity) { val stock = stockRepository.insertStock(stockDetails) .ifDuplicateGetExistingWith { stockRepository.getStockBy(stockDetails.symbol).trust() } .trust() while (true) { if (dataPeriodicity.isIntraday) { fetchAndStore( stock, dataPeriodicity, stockDataClient::getIntradayPriceDataForStock, intradayTimeSeriesRepository::insertNonExisting ) } else { fetchAndStore( stock, dataPeriodicity, stockDataClient::getInterdayPriceDataForStock, interdayTimeSeriesRepository::insertNonExisting ) } delay(fetchingInterval.inWholeMilliseconds) } }
这种方式简单直接,核心逻辑完全复用,没有冗余代码。
方式二:用密封类封装配对逻辑
如果想避免if-else的重复结构,可定义密封类封装fetch和store的配对逻辑,让分支返回统一的密封类实例:
// 定义抽象处理接口 sealed class DataHandler<X, R : Comparable<R>> { abstract suspend fun fetch(symbol: String, periodicity: StockDataPeriodicity): Either<Throwable, X> abstract fun store(stock: Stock, values: X): Either<Throwable, List<Entity<R>>> } // 日内数据处理器 class IntradayHandler( private val client: StockDataClient, private val repo: IntradayTimeSeriesRepository ) : DataHandler<StockPriceIntradayValues, Int>() { override suspend fun fetch(symbol: String, periodicity: StockDataPeriodicity): Either<Throwable, StockPriceIntradayValues> { return client.getIntradayPriceDataForStock(symbol, periodicity) } override fun store(stock: Stock, values: StockPriceIntradayValues): Either<Throwable, List<Entity<Int>>> { return repo.insertNonExisting(stock, values) } } // 日间数据处理器 class InterdayHandler( private val client: StockDataClient, private val repo: InterdayTimeSeriesRepository ) : DataHandler<StockPriceInterdayValues, Long>() { override suspend fun fetch(symbol: String, periodicity: StockDataPeriodicity): Either<Throwable, StockPriceInterdayValues> { return client.getInterdayPriceDataForStock(symbol, periodicity) } override fun store(stock: Stock, values: StockPriceInterdayValues): Either<Throwable, List<Entity<Long>>> { return repo.insertNonExisting(stock, values) } }
然后修改startFetching:
suspend fun startFetching(dataPeriodicity: StockDataPeriodicity) { val stock = stockRepository.insertStock(stockDetails) .ifDuplicateGetExistingWith { stockRepository.getStockBy(stockDetails.symbol).trust() } .trust() val handler = if (dataPeriodicity.isIntraday) { IntradayHandler(stockDataClient, intradayTimeSeriesRepository) } else { InterdayHandler(stockDataClient, interdayTimeSeriesRepository) } while (true) { val fetchedValues = handler.fetch(stockDetails.symbol, dataPeriodicity).trust() handler.store(stock, fetchedValues) delay(fetchingInterval.inWholeMilliseconds) } }
这种方式更适合后续扩展更多数据类型的处理逻辑,保持代码的可维护性。
关于共享接口的说明
你尝试让StockPriceIntradayValues和StockPriceInterdayValues实现共享接口,但出现type1 & type2的问题,是因为仓库的insertNonExisting函数参数是具体类型,编译器无法将其统一为接口类型。这种方式会丢失类型安全性,不推荐使用。
内容的提问来源于stack exchange,提问作者LeYAUable
相关产品推荐
相关产品推荐

