具有高阶功能的Kotlin合约

时间:2018-11-28 15:57:59

标签: generics kotlin kotlin-contracts

我对Kotlin可为空性有疑问,我想知道我是否可以通过合同解决它。 对于此类Java界面:interface Action<T>{ void execute(T param); }有两个扩展名:

fun <T, R> Action<T>.map(mapper:(R)->T): Action<R> {
   return Action{ execute(mapper(it)) }
}

fun <T> Action<T>.ifNotNull(): Action<T> {
  return Action { if(it != null) execute(it) }
} 

还有一个具有可空数据的通用模型:

class Model<T>(val data: T?)

现在,我创建了以Action接口为参数的函数。情况是仅在param != null时才执行操作参数,因此如下所示:

fun <T> callback(model: Model<T>, action: Action<T>){
    action
    .map{ it.getData() } //compilation error: getData return T? when action require T
    .ifNotNull() //execute only when data!=null
    .execute(model)
}

那么,现在是否可以使用Kotlin合约来确保编译器确保action不会使用null参数执行?

3 个答案:

答案 0 :(得分:1)

ModelAction在您自己的答案中只是为ifNotNull()提供了正确的签名:

fun <T> Action<T>.ifNotNull(): Action<T?> {
    return Action { if(it != null) execute(it) }
} 

那么您的操作顺序是错误的:

fun <T> callback(model: Model<T>, action: Action<T>){
    action
    .ifNotNull() // Action<T?>
    .map { model: Model<T> -> model.data } // Action<Model<T>>
    .execute(model)
}

请注意,编译器将无法为此R的使用推断map。您也可以将其写为

fun <T> modelAction(action: Action<T>): Action<Model<T>> {
    return action
    .ifNotNull()
    .map { it.data }
}

请注意,参数是map的“错误方法”;这种功能通常称为contramap

答案 1 :(得分:0)

如果不受限制,默认情况下所有类型参数都以 nullable 的形式提供(换句话说,它们是从Any?派生的)。解决此问题的简单方法只是在类型参数上指定非空绑定:<T : Any>

答案 2 :(得分:0)

我创建了专用于此用途的Action接口实现,如下所示:

class ModelAction<T>(val action: Action<T>) : Action<T?> {
    override fun execute(param: T?) {
        param?.let {
            action.execute(it)
        }
    }
}

fun callback<T>(model: Model<T>, action: Action<T>){
    ModelAction(action)
    .map{ it.getData() } 
    .execute(model)
}

这个想法可能会对有类似问题的人有所帮助,但是它不符合我的期望,我仍然依靠基于高阶函数的解决方案。