Java-Kotlin:泛型和方差
作者:互联网
我想在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说:
This is called declaration-site variance: we can annotate the type
parameter T of Source to make sure that it is only returned (produced)
from members of Source, and never consumed. To do this we provide
the out modifier
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看作是“您只能为其写入或分配一个值”,那么这是有道理的.有人可以解释吗?
解决方法:
在您的情况下,您实际上并没有使用type参数的方差:您永远不会传递值或使用从对e:KClass< T>的调用返回的值.
方差描述了您可以使用哪些值作为参数,以及在使用投影类型时(例如在函数实现内部)从属性和函数返回的值中可以期望得到什么.例如,其中KClass< T>将返回T(如签名中所写),KClass< out SomeType>.可以返回SomeType或其任何子类型.相反,当KClass< T>会期望参数T为SomeType中的KClass<期望SomeType的某些超类型(但确切地未知). 实际上,这定义了传递给此类函数的实例的实际类型参数的限制.对于不变类型KClass< Base> ;,您不能传递KClass< Super>或KClass< Derived> (其中派生:基数:超级).但是,如果函数期望KClass< out Base> ;,那么您也可以传递KClass< Derived> ;,因为它满足上述要求:它返回Derived从其应返回Base或其子类型的方法中得出(但对于KClass<超级).并且,相反,期望KClass< Base>的函数被设置为0.也可以接收KClass< Super>
因此,当您重写getCauseIfAssignableFrom以接受ae:KClass< Throwable>中的状态时,您声明在实现中希望能够将Throwable传递给某些通用函数或e的属性,并且需要一个具有以下功能的KClass实例:处理. Any :: class或Throwable :: class都适合,但这不是您所需要的.
由于您不调用e的任何函数,也不访问其任何属性,因此甚至可以将其类型设置为KClass< *>. (明确指出您不在乎类型是什么,并允许它为任何类型),它将起作用.
但是您的用例要求您将类型限制为Throwable的子类型.这是KClass< out Throwable>起作用:将类型参数限制为Throwable的子类型(再次,您声明,对于返回T的KClass< T>的函数和属性或带有T的某些函数(如Function< T>),您想使用返回值好像T是Throwable的子类型;尽管您没有这样做).
适用于您的另一个选项是定义上限< T:Throwable>.这类似于< out Throwable> ;,但是它另外捕获了KClass< T>的type参数.并允许您在签名的其他地方(返回类型或其他参数的类型)或实现内部使用它.
标签:kotlin,generics,covariance,java 来源: https://codeday.me/bug/20191025/1931315.html