Представьте, что у нас есть такая структура данных

sealed class ResultOf {
    class Success<out T>(
        val value: T
    ) : ResultOf()

    class Failure(
        val exception: Exception
    ) : ResultOf()
}

Это отлично работает:

inline fun <reified T> ResultOf.doIfFailure(callback: (exception: Exception) -> Unit) {
    if (this is ResultOf.Failure) {
        callback(exception)
    }
}

Но следующее не будет компилироваться из-за проблем с компиляцией ResultOf.Success & callback(value)

inline fun <reified T> ResultOf.doIfSuccess(callback: (value: T) -> Unit) {
    if (this is ResultOf.Success) {
        callback(value)
    }
}

Ожидается один аргумент типа. Используйте 'Success <*>', если вы не хотите передавать аргументы типа

Я предполагаю, что это как-то связано со стиранием шрифта? Но не могу найти ничего, что объясняло бы почему. Кроме того, есть ли способ обойти это? (Не помещая общий для ResultOf)?

0
enyciaa 9 Дек 2020 в 18:44

1 ответ

Лучший ответ

Реифицированные типы работают только во встроенных функциях. Типы классов никогда не реифицируются. Во время выполнения невозможно определить тип Success<T>.

Вы можете вручную отслеживать тип, добавляя параметр класса в конструктор класса и выполняя приведение в своей функции следующим образом.

sealed class ResultOf {
    class Success<out T: Any>(
            val cls: KClass<out T>,
            val value: T
    ) : ResultOf()

    class Failure(
            val exception: Exception
    ) : ResultOf()
}

inline fun <reified T> ResultOf.doIfSuccess(callback: (value: T) -> Unit) {
    if (this is ResultOf.Success<*> && cls == T::class) {
        callback(value as T)
    }
}

Однако это не лучший дизайн, потому что вы можете использовать doOnSuccess с неправильным типом значения, и он ничего не сделает даже в случае успеха. Я бы просто поместил этот тип в определение класса ResultOf. Тогда вам не нужно использовать овеществленную функцию или даже выполнять какое-либо приведение:

sealed class ResultOf<out T> {
    class Success<out T>(
            val value: T
    ) : ResultOf<T>()

    class Failure(
            val exception: Exception
    ) : ResultOf<Nothing>()
}

inline fun <T: Any> ResultOf<T>.doIfSuccess(callback: (value: T) -> Unit) {
    if (this is ResultOf.Success<T>) {
        callback(value)
    }
}
3
Tenfour04 9 Дек 2020 в 17:08