我正在尝试包装rxjava的timeout
method以使其可用for scala。
与many other methods类似,我试过这个:
def timeout[U >: T](timeout: Duration, other: Observable[U]): Observable[U] = {
val otherJava: rx.Observable[_ <: U] = other.asJavaObservable
val thisJava: rx.Observable[_ <: U] = this.asJavaObservable
toScalaObservable[U](thisJava.timeout(timeout.length, timeout.unit, otherJava))
}
但我收到以下错误:
Observable.scala:1631: error: overloaded method value timeout with alternatives:
($1: Long,x$2: java.util.concurrent.TimeUnit,x$3: rx.Scheduler)rx.Observable[_$85] <and>
($1: Long,x$2: java.util.concurrent.TimeUnit,x$3: rx.Observable[_ <: _$85])rx.Observable[_$85]
cannot be applied to (Long, scala.concurrent.duration.TimeUnit, rx.Observable[_$84])
toScalaObservable[U](thisJava.timeout(timeout.length, timeout.unit, otherJava))
原始java方法:
public Observable<T> timeout(long timeout, TimeUnit timeUnit, Observable<? extends T> other) {
return create(OperationTimeout.timeout(this, timeout, timeUnit, other));
}
我对Java和Scala(以及所有类型边界)都不是很熟悉,但据我所知:otherJava
和thisJava
都属于rx.Observable[U]
类型那么他们为什么不排队?
答案 0 :(得分:4)
嗯,你正在踩踏Scala中使用的Java泛型的方差问题。让我们一步一步走。
让我们来看看你的实现:
// does not compile (with your original error)
def timeout[U >: T](timeout: Duration, other: Observable[U]): Observable[U] = {
val otherJava: rx.Observable[_ <: U] = other.asJavaObservable
val thisJava: rx.Observable[_ <: U] = this.asJavaObservable
toScalaObservable[U](thisJava.timeout(timeout.length, timeout.unit, otherJava))
}
要理解为什么这不起作用,让我们在A
声明thisJava
中调用A <: U
未命名的类型,thisJava
使rx.Observable[A]
为{{1} }})。 timeout
thisJava: rx.Observable[A]
方法需要rx.Observable[_ <: A]
类型的参数,并给它类型rx.Observable[_ <: U]
:编译器无法知道这两种类型是如何相关的。它们可能根本不相关!
另一方面,如果A
为U
,那么thisJava
将为rx.Observable[U]
,其timeout
方法将为rx.Observable[_ <: U]
恰好是otherJava
的类型。我们试试吧:
// still does not compile, sadly
def timeout[U >: T](timeout: Duration, other: Observable[U]): Observable[U] = {
val otherJava: rx.Observable[_ <: U] = other.asJavaObservable
val thisJava: rx.Observable[U] = this.asJavaObservable // variance error
toScalaObservable[U](thisJava.timeout(timeout.length, timeout.unit, otherJava))
}
在一个完美的世界里,上述情况会起作用。 但是,java rx.Observable
未定义为协变,因为java中没有定义 - 站点方差注释。所以Scala认为它是不变的。
因此,就Scala而言,rx.Observable[_ <: U]
不一个rx.Observable[U]
,遗憾的是this.asJavaObservable
会返回rx.Observable[_ <: U]
。
但是我们知道[*] rx.Observable<T>
应该是协变的,所以我们可以盲目地抛弃:
// this compiles and *should* work
def timeout[U >: T](timeout: Duration, other: Observable[U]): Observable[U] = {
val otherJava: rx.Observable[_ <: U] = other.asJavaObservable
val thisJava = this.asJavaObservable.asInstanceOf[rx.Observable[U]]
toScalaObservable[U](thisJava.timeout(timeout.length, timeout.unit, otherJava))
}
这个故事的寓意是,混合Scala的方差和Java的方差总会让你在这里和那里付出一些代价,必须仔细考虑。
此外,让asJavaObservable
返回rx.Observable[T]
代替_ <: T
会使所有这些变得更容易,但也许有充分理由说明原因并非如此......
[*]更像是“但我怀疑”
答案 1 :(得分:3)
[这应该留在@ gourlaysama的答案的评论中,但我没有足够的声誉来评论]
@Aralo语句“MyType[_ <: T]
与MyType[T]
相同”仅在编译器知道 MyType
是协变的情况下才成立。这是List
的情况,因为它被定义为List[+A]
,但rx.Observable
不是这种情况,因为它是Java类型,因此它的类型参数不能有方差注释,所以编译器无法知道它是否应该是协变的。
@gourlaysama使asJavaObservable
返回rx.Observable[T]
而非_ <: T
不是解决方案,因为类型rx.lang.scala.Observable[T]
表示“T
或{属于T
“子类型的东西,此描述完全对应于类型rx.Observable[_ <: T]
(与rx.Observable<? extends T>
相同)。
我们必须使用Scala进行强制转换的原因是Java中timeout
的签名是“错误的”:严格来说,它使Java Observable不变,因为T
出现在逆变位置。 “正确”的方法是使用另一个类型参数U
,其下限为T
,如在Scala中,但Java不支持下限,因此最好的解决方案是保持“错误的“解决方案reduce
(请参阅this comment),onErrorReturn
和其他一些运算符也会出现此问题。
一般而言,所有这些应该具有较低有界类型参数(但不具有)的运算符只能在Observable<T>
上使用,而不能在Observable<? extends T>
上使用(非常严重的不便之处) Java用户),因此,他们需要在Scala适配器中进行强制转换。