Контракты Kotlin: экземпляр assert для параметра reified type - PullRequest
0 голосов
/ 08 ноября 2018

Я пытаюсь написать функцию assert, которая проверяет, имеет ли данный объект тип T:

@UseExperimental(ExperimentalContracts::class)
inline fun <reified T> assertIsInstance(value: Any?) {
    contract {
        returns() implies (value is T)
    }

    Assertions.assertThat(value).isInstanceOf(T::class.java)
}

Функция использует AssertJ для выполнения конкретного утверждения, но я хочу сообщить компилятору, что после его выполнения value имеет тип T, так что возможна интеллектуальная трансляция. Кажется, это не работает, потому что:

Error in contract description: references to type parameters are forbidden in contracts

Есть ли другой способ добиться такого поведения? В чем здесь проблема? Будет ли это возможно в конечном итоге?

(Использование Kotlin v1.3)

Ответы [ 3 ]

0 голосов
/ 08 ноября 2018

Это беспокоило меня пару часов, тем более что это возможно:

val x: Any = "string"
require(x is String)
val len = x.length

Компилятор явно способен понять это, так что это, вероятно, ограничение самих контрактов.

Я провел некоторое время, пытаясь найти обходные пути. Для справки:

@UseExperimental(ExperimentalContracts::class)
inline fun <reified T> assertIsInstance(value: Any?) {
    contract {
        returns() implies T::class.isInstance(value))
    }
    if(value !is T){
        throw java.lang.IllegalArgumentException("Incorrect type");
    }
}

"Неподдерживаемая конструкция"

@UseExperimental(ExperimentalContracts::class)
inline fun <reified T> assertIsInstance(value: Any?, condition: Boolean = value is T) {
    contract {
        returns() implies condition
    }
    if(!condition)
        throw IllegalArgumentException("Incorrect type");
}

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

Это была моя последняя попытка:

@UseExperimental(ExperimentalContracts::class)
inline fun assertIsInstance(value: Any?, cls: KClass<out Any>) {
    contract {
        returns() implies (cls.isInstance(value))
    }
    if(!cls.isInstance(value))
        throw IllegalArgumentException("");
}

Еще одна "неподдерживаемая конструкция".

Каким-то образом я закончил с этим:

@UseExperimental(ExperimentalContracts::class)
inline fun assertIsInstance(value: Any?) {
    contract {
        returns() implies (value.hashCode() == 0)
    }
    if(value.hashCode() != 0)
        throw java.lang.IllegalArgumentException();
}

Но это дает новую ошибку: only references to parameters are allowed in contract description.

TL; DR:

Не похоже, что ты можешь. Подстановка, как я сделал во втором примере, не вызывает умного приведения, а остальные не работают из-за различных ошибок компилятора.

По крайней мере, на данный момент пути не существует. Конечно, вы можете открыть вопрос в репозитории Kotlin и попросить что-то подобное, но пока это не представляется возможным.

0 голосов
/ 03 июля 2019

Разве оператор как не делает этого?

fun main() {
    val x: Any = "string"
    x as String
    val len = x.length
    println(len)
}

https://pl.kotl.in/uFCsGWEZm

0 голосов
/ 08 ноября 2018

В какой-то момент возникли некоторые (глубоко технические) опасения относительно поддержки таких конструкций в IDE, но возможно, что это ограничение будет смягчено в будущем.

...