【发布时间】:2017-08-08 23:57:34
【问题描述】:
我想在Throwable 上创建一个扩展函数,给定KClass,递归搜索与参数匹配的根本原因。以下是一种有效的尝试:
fun <T : Throwable> Throwable.getCauseIfAssignableFrom(e: KClass<T>): Throwable? = when {
this::class.java.isAssignableFrom(e.java) -> this
nonNull(this.cause) -> this.cause?.getCauseIfAssignableFrom(e)
else -> null
}
这也有效:
fun Throwable.getCauseIfAssignableFrom(e: KClass<out Throwable>): Throwable? = when {
this::class.java.isAssignableFrom(e.java) -> this
nonNull(this.cause) -> this.cause?.getCauseIfAssignableFrom(e)
else -> null
}
我这样调用函数:e.getCauseIfAssignableFrom(NoRemoteRepositoryException::class)。
但是,Kotlin docs 关于泛型说:
这称为声明站点差异:我们可以注释类型 Source 的参数 T 以确保它只被返回(产生) 来自 Source 的成员,从未消耗过。为此,我们提供 输出修饰符
abstract class Source<out T> {
abstract fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // This is OK, since T is an out-parameter
// ...
}
在我的例子中,参数e 不会被返回,而是被消耗掉。在我看来,它应该被声明为 e: KClass<in Throwable> 但这不会编译。但是,如果我将out 视为“您只能读取或返回它”,而将in 视为“您只能对其进行写入或赋值”,那么这是有道理的。谁能解释一下?
【问题讨论】:
-
嘿,刚刚注意到您的递归函数是序列的主要候选者:
generateSequence(this) { it.cause }.first { it::class.java.isAssignableFrom(e.java) }应该是行为兼容的,IMO 更快地消耗精神并且还摆脱了递归。但不要编辑问题,因为它可能会使当前答案无效。 -
您使用 Throwable 派生类调用您的方法,所以当您将参数定义为 KClass
时,您应该预期会出现编译错误,对吧? -
@mEQ5aNLrK3lqs3kfSa5HbvsTWe0nIu 一些用户名!关于顺序,我看到的问题是先收集所有原因,然后返回匹配的原因(如果有)。但是如果我们找到匹配项,就不需要再迭代了;您声称序列比递归更快的说法在理论上似乎并不成立。
-
@AbhijitSarkar
Sequence是懒惰的,它不会继续迭代。 -
通过编写测试验证该序列确实是惰性求值的。谢谢。
标签: java generics kotlin covariance