我有几点。首先,声称
一件事是我在 cons 和 #:: 行为中使用的界限,每个 MyStream 都会退化为 MyStream[Any]。
实际上是不正确的。您可以在此现场演示中亲自观看。请注意如何ssGood
轻松地将其分配给类型化ssGood2
而无需强制转换,并且您无法使用ssBad
显式类型化 as来执行此操作MyStream[Any]
。这里的重点是 Scala 编译器在这种情况下得到了非常正确的类型。我怀疑您的实际意思是 Intellij IDEA 推断出错误的类型并进行了一些不好的突出显示等。不幸的是,出于技术原因,IDEA 使用自己的编译器而不是标准编译器,并且有时在代码复杂时会出错。有时您实际上必须编译代码以查看它是否正确。
关于天真的泛型的第二个说法对我来说也不正确。
但是,如果我使用天真的泛型:
def cons[T](h: T, t: => MyStream[T]): MyStream[T] = new MyStream[T] ...
类型保持稳定,但我不能使用 cons / #:: 将任何内容附加到 MyStream.empty ...
当我使用以下代码时(可在线获取)
object MyStream {
val empty: MyStream[Nothing] = new MyStream[Nothing] {
override def isEmpty = true
override def head = throw new NoSuchElementException("tead of empty MyStream")
override def tail = throw new NoSuchElementException("tail of empty MyStream")
}
def cons[T](h: T, t: => MyStream[T]): MyStream[T] = new MyStream[T] {
def isEmpty = false
def head = h
lazy val tail = t
}
implicit class MyStreamOps[T](t: => MyStream[T]) {
def #::(h: T): MyStream[T] = cons(h, t)
}
}
abstract class MyStream[+T] {
def isEmpty: Boolean
def head: T
def tail: MyStream[T]
@tailrec final def foreach(op: T => Unit): Unit = {
if (!isEmpty) {
op(head)
tail.foreach(op)
}
}
}
import MyStream._
val ss0 = 1 #:: empty
val ss1: MyStream[Int] = ss0
val ss2: MyStream[Int] = 1 #:: empty
只要有声明,它就可以[+T]
对
我编译和运行。MyStream[+T]
而这一次我不确定你到底做错了什么(而且你没有提供任何实际的编译器错误,所以很难猜测)。
此外,如果您empty
是非泛型和不可变的,则不需要它def
- 它也可以val
。
如果您仍然有一些问题,您可能应该提供有关如何重现它以及您遇到什么错误的更多详细信息。
更新(回复评论)
托比,对不起,我还是不明白你的问题 #2。您能否提供一个未在您的问题中编译的代码示例或作为评论?
我唯一的猜测是,您的意思是,如果您T
在主要答案中仅使用一个泛型的代码,则这样的一段代码将失败:
def test() = {
import MyStream._
val ss0: MyStream[String] = "abc" #:: empty
val sb = new StringBuilder
val ss1: MyStream[CharSequence] = ss0 //OK
val ss2: MyStream[CharSequence] = cons(sb, ss0) //OK
val ss3: MyStream[CharSequence] = sb #:: ss0 //Bad?
}
是的,这是真的,因为 AFAIU Scala 编译器在检查隐式包装器时不会尝试遍历所有泛型类型的所有可能替代品,而只使用最具体的替代品。因此ss0
尝试转换为MyStreamOps[String]
但不转换为MyStreamOps[CharSequence]
. 要解决该问题,您需要在 中添加另一个泛型类型,U >: T
但不必添加到. 所以用下面的定义#::
MyStreamOps
cons
MyStream
object MyStream {
val empty: MyStream[Nothing] = new MyStream[Nothing] {
override def isEmpty = true
override def head = throw new NoSuchElementException("tead of empty MyStream")
override def tail = throw new NoSuchElementException("tail of empty MyStream")
}
def cons[T](h: T, t: => MyStream[T]): MyStream[T] = new MyStream[T] {
def isEmpty = false
def head = h
lazy val tail = t
}
implicit class MyStreamOps[T](t: => MyStream[T]) {
//def #::(h: T): MyStream[T] = cons(h, t) // bad
def #::[U >: T](h: U): MyStream[U] = cons(h, t) //good
}
}
即使ss3
编译没有错误(并且即使没有完全因为工作ss2
也使用编译)。cons
U
+T