Использование Kotlin контрактов для приведения типа внутри предиката функции Iterable - PullRequest
1 голос
/ 06 мая 2020

У меня есть этот запечатанный класс PictureEvent:

sealed class PictureEvent {

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

    //more classes extending PictureEvent

}

Теперь из списка PictureEvent s я хочу получить первый 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 Ответ

4 голосов
/ 06 мая 2020

Контракт работает нормально, однако, если вы посмотрите на подпись метода 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>()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...