我是scala的新手。要温柔。 我认为问题在于我创建了一个与scala.collection.immutable.List
冲突的特质列表trait List[T] {
def isEmpty: Boolean
def head[T]
def tail[T]
}
class Cons[T](val head:T, val tail: Option[List[T]]) extends List[T] {
def isEmpty: Boolean = false
def head[T] = head
def tail[T] = tail
}
class Nil[T] extends List[T] {
def isEmpty: Boolean = true
def head[T] = throw new IllegalStateException("head")
def tail[T] = throw new IllegalStateException("tail")
}
// ERROR HERE
// obj with one elem
val one: Cons[Int] = new Cons[Int](1,Option(scala.List[Int]()))
错误是:预期的scala.Option [List [Int]],实际 scala.Option [scala.collection.immutable.List [Int]]
谢谢。
答案 0 :(得分:2)
您已声明tail
的类型为Option[List[T]]
,但是您将调用Option(scala.List[Int]())
的结果传递给了它,这是Option.apply(scala.List[Int].apply())
的语法糖。
scala.List[Int].apply
返回scala.collection.immutable.List[Int]
,而不是List[Int]
。我不确定为什么您会期望使用内置的Scala方法来返回一个完全不了解的对象。无论如何,针对此特定错误的正确解决方案是传递您的 Nil
类的实例,而不是尝试传递 Scala的 Nil
。 / p>
这与您为性状选择名称List
无关。您使用其他任何名称都将遇到相同的错误。
让我们仔细看看您的代码:
trait List[T] {
def isEmpty: Boolean
def head[T]
def tail[T]
}
您没有为head
和tail
声明类型。这意味着将要使用的默认类型为Unit
,这意味着这些方法不会返回任何内容,它们只会产生副作用。因此,以下内容完全合法:
class Cons extends List[Int] {
override def isEmpty = false
override def head[Int] = Unit
override def tail[Int] = Unit
}
实际上,由于返回类型为Unit
意味着返回值只是被丢弃,即使这是合法的:
class Cons extends List[Int] {
override def isEmpty = false
override def head[Int] = "Hello"
override def tail[Int] = true
}
所以,您可能想要声明返回类型:
trait List[T] {
def isEmpty: Boolean
def head[T]: T
def tail[T]: T
}
另一个问题是,您要使用类型参数T
声明一个特征,然后使用一个不同类型参数声明一个方法head
,而恰好具有相同的特征名称,并因此遮蔽外部T
,并声明具有另一个不同类型参数的方法tail
。
这完全等同于
trait List[T] {
def isEmpty: Boolean
def head[U]
def tail[V]
}
三个类型参数之间的关系完全为零。
这意味着例如以下类完全合法:
class Cons extends List[Int] {
override def isEmpty = false
override def head[String] = "Hello"
override def tail[Float] = 1.23
}
事实上,由于您从未在任何地方使用T
,因此可以将其省略:
trait List {
def isEmpty: Boolean
def head[U]
def tail[V]
}
对于U
和V
同样如此,因此您实际上 所写的内容或多或少是这样的:
trait List {
def isEmpty: Boolean
def head
def tail
}
但是我想那根本不是您想要的!
似乎您实际上希望所有三个T
都相同。但!因为head
是单个元素而tail
是一个列表(或者在您的情况下是可选列表),所以这可能行不通……单个元素和列表选项如何才能具有完全相同的类型?那是不可能的!
您(可能)想要的是这样的东西:
trait List[T] {
def isEmpty: Boolean
def head: T
def tail: Option[List[T]]
}
但是请注意,Option
也没有意义,因为您以后实际上创建了一个特殊类来表示一个空列表(Nil
),因此您不需要Option
在这里。这将造成极大的混乱,因为您既可以有一个现有的空列表,也可以有一个不存在的列表,但是它们都具有相同的含义。让我们摆脱Option
:
trait List[T] {
def isEmpty: Boolean
def head: T
def tail: List[T]
}
最后,您会发现我们只能从列表中的 中删除元素,而永远不能在中放入。 (我们只能构造具有其他元素的 new 列表。)这意味着我们的列表实际上是其元素类型中的 covariant ,因此,请确保Scala知道这一点:>
trait List[+T] {
def isEmpty: Boolean
def head: T
def tail: List[T]
}
此外,请注意,您要确保只有两个子类:空列表和非空列表。您不想有第三种列表。因此,应将其设置为sealed trait
,以便只能在同一编译单元内对其进行子类化:
sealed trait List[+T] {
def isEmpty: Boolean
def head: T
def tail: List[T]
}
现在,让我们看一下Cons
类。
class Cons[T](val head:T, val tail: Option[List[T]]) extends List[T] {
def isEmpty: Boolean = false
def head[T] = head
def tail[T] = tail
}
同样,您在这里也有同样的阴影。您不需要三个不同的类型参数,而只需要一个:
class Cons[+T](val head: T, val tail: Option[List[T]]) extends List[T] {
def isEmpty = false
def head = head
def tail = tail
}
还请注意,在您的原始版本中,head
和tail
的返回类型均为Unit
,因此返回值将被丢弃。
再次,让我们摆脱Option
,因为我们知道,总会有一个tail
,这就是我们Nil
的目的,代表一个空的tail
:
class Cons[+T](val head: T, val tail: List[T]) extends List[T] {
def isEmpty = false
def head = head
def tail = tail
}
此外,构造函数中的val
声明已经创建了一个getter方法,因此我们可以摆脱这些方法:
class Cons[+T](override val head: T, override val tail: List[T]) extends List[T] {
override def isEmpty = false
}
还请注意,我们并未在isEmpty
中进行任何计算,因此无需将其作为一种方法来每次重新评估false
;我们可以简单地将其设置为字段:
class Cons[T](override val head: T, override val tail: List[T]) extends List[T] {
override val isEmpty = false
}
同样,我们不希望有人创建Cons
的另一个子类,因为我们要确保只有两个。因此,我们将Cons
设为final
类:
final class Cons[+T](override val head: T, override val tail: List[T]) extends List[T] {
override val isEmpty = false
}
为方便起见,我们也将其设置为case class
,这样我们就可以对构造函数参数使用模式匹配,并获得toString()
和##
的一个很好的自动定义的实现,并且带有apply
工厂方法的伴随对象:
final case class Cons[+T](override val head: T, override val tail: List[T]) extends List[T] {
override val isEmpty = false
}
最后,所有这些同样适用于Nil
,我将不再重复相同的详细步骤:
final case class Nil[+T]() extends List[T] {
override val isEmpty = true
override def head: Nothing = throw new IllegalStateException("head")
override def tail: Nothing = throw new IllegalStateException("tail")
}
但是,请注意,没有“整数空列表”,“字符串空列表”等是没有意义的。空列表是空列表。期。空列表只有一个。
因此,让我们将其设为对象:
case object Nil extends List[Nothing] {
override val isEmpty = true
override def head = throw new IllegalStateException("head")
override def tail = throw new IllegalStateException("tail")
}
之所以可行,是因为我们的列表是协变的(如果List[X]
是List[Y]
的子类型,则X
是Y
的子类型)和{{1} }是每种类型的子类型;因此Nothing
是每个List[Nothing]
的子类型,因此我们可以在期望List[T]
的任何地方替换Nil
,而不管List[T]
是什么。
最后,我们可能想为T
随播对象提供List
工厂方法,以方便地构造apply
:
List
总共,我们的代码现在看起来像这样:
object List {
def apply[T](elements: T*) = elements.foldRight(Nil: List[T])(Cons.apply)
}
答案 1 :(得分:1)
我认为您想要的是:
val one: Cons[Int] = new Cons[Int](1, None)
或者,如果您希望包含多个元素的列表:
val two: Cons[Int] = new Cons[Int](1, Some(new Cons[Int](2, None)))
答案 2 :(得分:0)
好的,我相信(因为我以某种方式覆盖了类List),我应该一直使用自定义的“构造函数”。所以这是正确的代码行:
val one: Cons[Int] = new Cons[Int](1,Option(new Nil[Int]()))