Kotlin合约:在类型化参数

时间:2018-11-08 10:29:15

标签: generics kotlin assertj kotlin-reified-type-parameters kotlin-contracts

我正在尝试编写一个断言函数,以检查给定对象是否为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 :(得分:2)

在某些时候,关于在IDE中对此类结构的支持存在一些(深层次的技术)问题,但将来有可能会放宽此限制。

答案 1 :(得分:1)

这困扰了我几个小时,尤其是因为这是可能的:

$service = new Google_Service_Analytics( $client );

编译器显然能够理解这些内容,因此这很可能是合同本身的限制。

我花了一段时间来尝试一些解决方法。供参考:

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(); }

TL; DR:

看起来好像不行。像我在第二个示例中所做的那样偷偷摸摸,不会触发智能强制转换,并且由于各种编译器错误,其余代码无法正常工作。

至少到目前为止,似乎还没有办法。您当然可以在Kotlin存储库中打开一个问题,并要求类似的内容,但目前看来,这似乎是不可能的。

答案 2 :(得分:0)

as 运算符不这样做吗?

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

https://pl.kotl.in/uFCsGWEZm