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

Kotlin中如何封装带具体化类型参数的内联方法withCatchingErrors

解决方案:封装带reified泛型的私有inline函数

你遇到的核心限制是:Kotlin的public inline函数无法直接调用private inline函数——因为inline函数的代码会被内联到外部调用方的作用域,而private函数对外部不可见。而你的withCatchingErrors()因需要reified泛型做类型检查,必须保留inline关键字。以下是三种可行的封装方案:


方案1:将withBreakableFlow()改为非inline函数(优先推荐,如果允许)

如果withBreakableFlow()不需要是inline函数(比如不需要支持非局部返回等inline专属特性),直接将其设为public non-inline,同时保留withCatchingErrors()为private inline即可。非inline函数在类内部调用private inline函数完全合法,内联仅发生在类内部代码中,外部调用方不会接触到私有逻辑。

object FlowUtils {
    // 公开的非inline方法
    public fun <T> withBreakableFlow(
        block: suspend FlowBreakScope.() -> T
    ): Flow<T> = flow {
        val scope = object : FlowBreakScope {
            override fun <E> breakFlow(error: E) {
                throw FlowBreakException(error)
            }

            override fun <E> catchError(block: () -> Unit) {
                // 内部调用私有inline函数
                withCatchingErrors<E>(block)
            }
        }
        emit(block(scope))
    }

    // 私有inline函数,带reified泛型做类型检查
    private inline fun <reified E> withCatchingErrors(action: () -> Unit) {
        try {
            action()
        } catch (e: AppErrorAsThrowable) {
            if (e.data is E) {
                throw FlowBreakException(e.data)
            } else {
                throw e
            }
        }
    }
}

// 辅助定义
interface FlowBreakScope {
    fun <E> breakFlow(error: E)
    fun <E> catchError(block: () -> Unit)
}

class FlowBreakException(val error: Any?) : Throwable()
class AppErrorAsThrowable(val data: Any?) : Throwable()

方案2:将withCatchingErrors()定义为局部inline函数

如果withBreakableFlow()必须是inline函数,可以把withCatchingErrors()作为前者的局部inline函数。局部函数仅在包含它的函数内部可见,同时因为是inline的,reified泛型可以正常工作;public inline函数内联时会将局部函数的代码一并包含进去,外部完全无法访问这个私有逻辑。

object FlowUtils {
    // 公开的inline方法
    public inline fun <T> withBreakableFlow(
        crossinline block: suspend FlowBreakScope.() -> T
    ): Flow<T> = flow {
        // 局部inline函数,仅当前方法内部可见
        inline fun <reified E> withCatchingErrors(action: () -> Unit) {
            try {
                action()
            } catch (e: AppErrorAsThrowable) {
                if (e.data is E) {
                    throw FlowBreakException(e.data)
                } else {
                    throw e
                }
            }
        }

        val scope = object : FlowBreakScope {
            override fun <E> breakFlow(error: E) {
                throw FlowBreakException(error)
            }

            override fun <E> catchError(block: () -> Unit) {
                withCatchingErrors<E>(block)
            }
        }
        emit(block(scope))
    }
}

// 辅助定义同上
interface FlowBreakScope {
    fun <E> breakFlow(error: E)
    fun <E> catchError(block: () -> Unit)
}

class FlowBreakException(val error: Any?) : Throwable()
class AppErrorAsThrowable(val data: Any?) : Throwable()

方案3:用@PublishedApi标记为internal(模块化场景)

如果你的代码是模块化的,且希望在模块内复用withCatchingErrors()但对外隐藏,可以用@PublishedApi注解将其标记为internal。这个注解允许public inline函数访问internal成员,同时外部模块无法直接调用该函数,达到模块级封装效果。

object FlowUtils {
    // 公开的inline方法
    public inline fun <T> withBreakableFlow(
        crossinline block: suspend FlowBreakScope.() -> T
    ): Flow<T> = flow {
        val scope = object : FlowBreakScope {
            override fun <E> breakFlow(error: E) {
                throw FlowBreakException(error)
            }

            override fun <E> catchError(block: () -> Unit) {
                withCatchingErrors<E>(block)
            }
        }
        emit(block(scope))
    }

    // @PublishedApi允许inline函数访问,对外(模块外)不可见
    @PublishedApi
    internal inline fun <reified E> withCatchingErrors(action: () -> Unit) {
        try {
            action()
        } catch (e: AppErrorAsThrowable) {
            if (e.data is E) {
                throw FlowBreakException(e.data)
            } else {
                throw e
            }
        }
    }
}

// 辅助定义同上

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 18:05:53