1

我有这个密封类PictureEvent

sealed class PictureEvent {

    data class PictureCreated(val pictureId: String, val url: String) : PictureEvent()

    //more classes extending PictureEvent

}

现在,从PictureEvents 列表中,我想获得第一个PictureCreated

fun doSomething(events: List<PictureEvent>) {

  val creationEvent = events.first { isCreationEvent(it) } as PictureEvent.PictureCreated

  //do stuff with the creationEvent

}

private fun isCreationEvent(event: PictureEvent) : Boolean {
  return event is PictureEvent.PictureCreated
}

它工作正常。如您所见,我将事件转换为PictureCreated(使用as关键字),因为first方法返回一个PictureEvent. 我想知道是否可以通过使用 Kotlin 合约来避免这种转换。

我试过这个:

private fun isCreationEvent(event: PictureEvent) : Boolean {
  contract {
    returns(true) implies (event is PictureEvent.PictureCreated)
  }
  return event is PictureEvent.PictureCreated
}

但它不起作用;first方法不断返回 a PictureEvent,而不是PictureCreated. 目前有可能做到这一点吗?

4

1 回答 1

4

合同工作正常,但是如果您查看first方法签名,您应该能够理解正在发生的事情以及为什么找到的对象不是自动转换的:

public inline fun <T> Iterable<T>.first(predicate: (T) -> Boolean): T

在您的情况下,该方法的返回类型与为实例first中所有元素定义的返回类型相同,并且在谓词中没有自动转换可以不幸地改变它。IterablePictureEvent


例如,您可以先按所需的类类型过滤列表,而不是合同,然后获取第一个元素:

val creationEvent = events
    .filterIsInstance(PictureEvent.PictureCreated::class.java)
    .first()

或创建您自己的扩展,类似于first

inline fun <reified R> Iterable<*>.firstOfInstance(): R {
    val first = first { it is R }
    return first as R
}

// or wrapping filterIsInstance
inline fun <reified R> Iterable<*>.firstOfInstance(): R {
    return filterIsInstance(R::class.java).first()
}

val creationEvent = events.firstOfInstance<PictureEvent.PictureCreated>()
于 2020-05-06T17:40:06.107 回答