所以,让我们说我有一个带有逆变型参数的类:
trait Storage[-T] {
def list(path: String): Seq[String]
def getObject[R <: T](path: String): Future[R]
}
type参数的想法是将实现约束到它可以返回的类型的上边界。因此,Storage[Any]
可以读取任何内容,而Storage[avro.SpecificRecord]
可以读取avro记录,但不能读取其他类:
def storage: Storage[avro.SpecificRecord]
storage.getObject[MyAvroDoc]("foo") // works
storage.getObject[String]("bar") // fails
现在,我有一个实用工具类,可用于迭代给定位置的对象:
class StorageIterator[+T](
val storage: Storage[_ >: T],
location: String
)(filter: String => Boolean) extends AbstractIterator[Future[T]] {
val it = storage.list(location).filter(filter)
def hasNext = it.hasNext
def next = storage.getObject[T](it.next)
}
这有效,但有时我需要从迭代器下游访问底层storage
,从辅助位置读取另一种类型的对象:
def storage: Storage[avro.SpecificRecord]
val iter = new StorageIterator[MyAvroDoc]("foo")
iter.storage.getObject[AuxAvroDoc](aux)
当然,这不起作用,因为storage
类型参数是通配符,并且没有证据表明它可用于阅读AuxAvroDoc
我试着像这样解决它:
class StorageIterator2[P, +T <: P](storage: Storage[P])
extends StorageIterator[T](storage)
这有效,但现在我必须在创建它时指定两种类型的参数,这很糟糕:(
我试图通过向Storage
本身添加一个方法来解决它:
trait Storage[-T] {
...
def iterate[R <: T](path: String) =
new StorageIterator2[T, R](this, path)
}
但这并没有编译,因为它将T
置于一个不变的位置:(
如果我使P
逆变,那么StorageIterator2[-P, +T <: P]
会失败,因为它会认为P occurs in covariant position in type P of value T
。
这个最后一个错误我不明白。为什么P
在这里不能反变?如果这个位置真的是协变的(为什么会这样?)那为什么它允许我在那里指定一个不变量参数呢?
最后,有没有人知道如何解决这个问题? 基本上,这个想法是能够
storage.iterate[MyAvroDoc]
而不必再次给它上限,iterator.storage.getObject[AnotherAvroDoc]
而不必转储存储以证明它可以读取此类对象。任何想法都表示赞赏。
答案 0 :(得分:1)
StorageIterator2[-P, +T <: P]
失败,因为它没有意义。如果您有StorageIterator2[Foo, Bar]
和Bar <: Foo
,那么因为它在第一个参数中是逆变的,所以它也是StorageIterator[Nothing, Bar]
,但是Nothing
没有子类型,所以它是Bar <: Nothing
在逻辑上是不可能的,但是StorageIterator2
这是必须的。因此,StorageIterator2
不能存在。
根本问题是Storage
不应该是逆变的。根据它给用户的合同,想一想Storage[T]
是什么。 Storage[T]
是您提供路径的对象,并将输出T
。 Storage
具有协变性是完全合理的:例如,知道如何输出String
的东西也输出Any
s,因此从逻辑意义上讲{{1} }。你说它应该是另一种方式,Storage[String] <: Storage[Any]
应该知道如何输出Storage[T]
的任何子类型,但是它会如何工作?如果有人在事后添加了一个子类型到T
怎么办?由于单例类型,T
甚至可以T
并且仍然存在此问题。这是不必要的复杂,并反映在您的问题中。也就是说,final
应为
Storage
这不会打开您在问题中给出的示例错误:
trait Storage[+T] {
def list(path: String]: Seq[String]
def get(path: String): T
}
现在,您的问题是,您无法执行val x: Storage[avro.SpecificRecord] = ???
x.get(???): avro.SpecificRecord // ok
x.get(???): String // nope
(x: Storage[String]).get(???) // nope
之类的操作,并且必须隐式转换。您可以改为storage.getObject[T]
:
match
普通storage.getObject(path) match {
case value: CorrectType => ...
case _ => // Oops, something else. Error?
}
(不受欢迎),或者您可以向asInstanceOf
添加辅助方法,就像之前的方法一样:
Storage