Kotlin Contracts: утверждение экземпляра по параметру повторного типа

Я пытаюсь написать функцию 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)


person s1m0nw1    schedule 08.11.2018    source источник


Ответы (3)


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

person Dmitry Savvinov    schedule 08.11.2018
comment
Спасибо за ответ. Есть ли альтернативный подход к реализации такого контракта? - person s1m0nw1; 08.11.2018
comment
@ s1m0nw1, к сожалению, на данный момент обойти это невозможно. Вы можете отслеживать статус этого запроса функции здесь: youtrack.jetbrains.com/issue/KT-28298 - person Dmitry Savvinov; 19.11.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 и попросить что-то подобное, но на данный момент это кажется невозможным.

person Zoe    schedule 08.11.2018

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

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

https://pl.kotl.in/uFCsGWEZm

person Rupert Rawnsley    schedule 03.07.2019