Использование контрактов Kotlin для приведения типа внутри предиката функции Iterable
У меня этот закрытый класс 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 ответ
Контракт работает нормально, однако, если вы посмотрите на 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>()