Использование контрактов Kotlin для приведения типа внутри предиката функции Iterable

У меня этот закрытый класс 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 метод продолжает возвращать PictureEvent, вместо того PictureCreated. Возможно ли это сделать сейчас?

1 ответ

Решение

Контракт работает нормально, однако, если вы посмотрите на first подпись метода, вы должны понимать, что происходит и почему найденный объект не применяется автоматически:

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

Тип возврата first тот же метод, что и для всех элементов в Iterable пример, PictureEvent в вашем случае, и никакое автокастирование внутри предиката, к сожалению, не может это изменить.


Например, вместо контрактов вы можете сначала отфильтровать свой список по желаемому типу класса, а затем взять первый элемент:

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>()
Другие вопросы по тегам