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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 22:27:31