641 字
3 分钟
浅谈Kotlin中的泛型和类型转换

起因#

由于我的项目分割开了common模块和其他子模块, 子模块需要使用common相同的代码, 但是提供的类型又不是统一的, 所以就引入了泛型 (generics)这个概念

刚开始学Kotlin的时候觉得泛型很难, 但是现在看起来泛型其实还挺好理解的(但是协变和逆变还是没理解, 因为没有用到)

泛型和类型转换的对比#

泛型的本质是类型擦除(Type Erasure)。这意味着,在编译时,泛型类型会被擦除为其原始类型。例如, List<Int>List<String> 在运行时都变成了 List 类型。

而类型转换是通过as操作符来显式的进行转换, as操作的时候需要进行类型检查, 这会消耗一小部分资源 (微乎其微)但是体现在大项目中就不是这么回事了

代码部分#

假设我们需要一个命令执行拦截器, 拦截器的函数就需要传入匹配到的命令以及触发命令的消息 而消息又细分为GroupPrivate分别代表群聊和私聊消息, 既然是在common模块中 那么这三种类型就不存在, 因为这三种类型只存在于子模块中, 所以需要在common模块中 定义三个接口分别代表这三种类型, 然后让子模块中的具体类型实现这三个接口。

在定义泛型的时候可以指定类型约束

interface IExecutionInterceptor<B : IBaseCommand, G : IGroupMessage, P : IPrivateMessage> {
    /**
     * 在群组命令执行之前执行, 可以返回[CommandResult]中的枚举类
     * 来确定是否继续执行这条命令
     * @param command 触发拦截器的命令
     */
    suspend fun beforeGroupExecute(message: G, command: B): CommandResult {
        return CommandResult.CONTINUE
    }

    /**
     * 群组命令执行之后要执行的代码片段
     * @param command 触发拦截器的命令
     */
    suspend fun afterGroupExecute(message: G, command: B) {}

    /**
     * 在私聊命令执行之前执行, 可以返回[CommandResult]中的枚举类
     * 来确定是否继续执行这条命令
     * @param command 触发拦截器的命令
     */
    suspend fun beforePrivateExecute(message: P, command: B): CommandResult {
        return CommandResult.CONTINUE
    }

    /**
     * 私聊命令执行之后要执行的代码片段
     * @param command 触发拦截器的命令
     */
    suspend fun afterPrivateExecute(message: P, command: B) {}
}

子模块中的impl

/**
 * 实现了拦截器
 */
abstract class ExecutionInterceptor : IExecutionInterceptor<BaseCommand, GroupMessage, PrivateMessage>

这里指定了具体的类型, 用户只要继承这个抽象类就能重写IExecutionInterceptor中的公开方法