Bur*_*urg 5 kotlin kotlin-reflect
KClass定义为public interface KClass<T : Any> : KDeclarationContainer, KAnnotatedElement, KClassifier
这很棘手,因为 a 的类String?应该是KClass<String>,但不可能获得。
给出下面的 3 个示例(它们本质上应该执行相同的工作),其中 1 个无法编译,其他返回相同的运行时类型。
inline fun <reified T> test1(): Any = T::class
inline fun <reified T: Any> test2(): KClass<T> = T::class
inline fun <reified T> test3(): KClass<T> = T::class // does not compile
test1<String?>() // class kotlin.String
test1<String>() // class kotlin.String
test2<String?>() // does not compile
test2<String>() // class kotlin.String
Run Code Online (Sandbox Code Playgroud)
问题的重点是问:如何获得 的运行时行为和test1的编译时行为(和安全性)test2?
编辑:问题的最后一个附录是另一个示例,它演示了获取可为空类型的类的问题。
inline fun <reified T> test4() {
val x = T::class // compiles, implied type of x is KClass<T>
val y: KClass<T> = T::class // does not compile with explicit type of KClass<T>
}
Run Code Online (Sandbox Code Playgroud)
我特别遇到问题的调用站点是这样的:
class OutputContract<T>(
private val output: () -> T,
val outputType: KClass<T> // ERROR!
) {
fun invoke(): T {
return output()
}
}
inline fun <reified T> output(noinline output: () -> T): OutputContract<T> {
return OutputContract(output, T::class)
}
Run Code Online (Sandbox Code Playgroud)
这里唯一的错误是 with KClass<T>,而不是 with T::class,后者运行得很好。我希望允许消费者将可空性指定为合同的一部分,因此添加Any约束将不起作用。如果我只是KClass<T>变成KClass<Any>,这一切都有效(这证明不存在运行时问题,只有编译时问题)。这是我最终选择的解决方法,但如果我能够真正维护正确的类型,那就太好了。
你的问题缺乏最重要的信息。您展示了一种人为地调用函数的情况,myFun<String?>()但如果是这种情况,您显然可以将其更改为不使用可为空类型。所以这可能不是真正的用例。您过于简化了您的解释,并删除了我们回答您的问题所需的最相关信息:“完整的方法签名是什么以及调用站点是什么样的? ”
缺少的是你如何推断你的类型T?您可以从返回值、方法参数中获取它,或者通过在每个调用站点中显式声明它来获取它。因此,您可以选择T: Any在您的函数中使用 a ,并决定哪个最好取决于您在问题中未显示的信息。
所以这是你的选择:
如果您根据返回参数推断类型,则允许返回可为空,但不要使具体化类型可为空:
// call site, any return type nullable or not
val something: String? = doSomething()
// function
inline fun <reified T: Any> doSomething(): T? {
val x: KClass<T> = T::class
// ...
}
Run Code Online (Sandbox Code Playgroud)或者,如果您从传入参数推断它,请执行相同的技巧:
// call site, any parameter type nullable or not
val param: String? = "howdy"
doSomethingElse(param)
// function
inline fun <reified T: Any> doSomethingElse(parm: T?) {
val x: KClass<T> = T::class
// ...
}
Run Code Online (Sandbox Code Playgroud)或者您从字面上指定通用参数(只是在输入参数名称时不要使其可为空):
// call site, any non-nullable generic parameter
doSomething<String>()
// function
inline fun <reified T: Any> doSomethingElse() {
val x: KClass<T> = T::class
// ...
}
Run Code Online (Sandbox Code Playgroud)或者,如果您无法更改通用参数,请使用星形投影(但为什么不能?!?):
// call site: whatever you want it to be
// function:
inline fun <reified T> test4() {
val x = T::class // compiles, implied type of x is KClass<T>
val y: KClass<*> = T::class KClass<T>
}
Run Code Online (Sandbox Code Playgroud)
顺便说一句,x和 的y行为相同,并且参考中将缺少一些方法/属性KClass。
这些例子中有四分之三可以满足你的愿望,我无法想象其中一个不起作用的情况。否则,你如何推断类型T?与上述不兼容的模式是什么?
注意在同一方法签名中使用<T: Any>with的技巧。T?
根据您对问题的上次更新,这将保持您对output函数引用的预期为空性,但允许它适用于KClass:
class OutputContract<T: Any>(private val output: () -> T?, val outputType: KClass<T>) {
fun invoke(): T? {
return output()
}
}
inline fun <reified T: Any> output(noinline output: () -> T?): OutputContract<T> {
return OutputContract(output, T::class)
}
Run Code Online (Sandbox Code Playgroud)
用户仍然可以通过传入不返回 null 的输出实现来控制可为 null 性,Kotlin 仍会对其进行类型检查并正常运行。但是必须检查对 invoke 的调用,因为它始终被假定为可为空。您实际上不能同时拥有这两种方式,想要启用可空性控制T,但在内部将其用作类型KClass,但您可以将其KClass<*>用作取决于您在KClass. 您可能不会错过任何重要的事情。您没有表明您打算如何处理,KClass因此很难就该主题说更多内容。 KClass如果您认为它们可能会向您传递泛型类,则通常不是一个好的类型,您应该使用 aKType代替。
| 归档时间: |
|
| 查看次数: |
3287 次 |
| 最近记录: |