7

我正在尝试编写一个断言函数来检查给定对象是否属于类型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 进行具体断言,但我愿意让编译器知道在其执行之后,valueis 的类型T以便可以进行智能转换。这似乎不起作用,因为:

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

还有另一种方法可以实现这种行为吗?这里有什么问题?这最终会成为可能吗?

(使用 Kotlin v1.3)

4

3 回答 3

3

在某些时候,对于在 IDE 中支持此类构造存在一些(深度技术性的)担忧,但这种限制可能会在未来放宽。

于 2018-11-08T11:40:41.790 回答
3

这一直困扰着我几个小时,特别是因为这是可能的:

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;博士:

看起来你不能。像我在第二个示例中所做的那样潜入它不会触发智能转换,并且由于各种编译器错误,其余部分不起作用。

至少目前看来,没有办法。你当然可以在 Kotlin 存储库中打开一个问题并要求这样的东西,但目前看来,这似乎是不可能的。

于 2018-11-08T15:23:32.757 回答
0

as运算符不这样做吗?

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

https://pl.kotl.in/uFCsGWEZm

于 2019-07-03T08:11:53.567 回答