Scala实现自定义List遇类型错误:期望List[?U1]却得到scala.collection.immutable.::[B]
解决自定义List时
::方法参数不匹配的问题 首先咱们先把问题的核心点掰明白:Scala里的::操作符(对应列表的Cons构造逻辑),设计初衷就是把单个元素加到列表的头部,所以它的参数必须是单个T类型的元素,而不是整个List[T]。你遇到的错误,就是因为不小心把一整个列表传给了::,完全不符合它的参数要求。
第一步:先搭好自定义List的基础结构
自定义List一般用递归的代数数据类型实现,大致框架是这样的:
sealed trait MyList[+T] case object MyNil extends MyList[Nothing] case class MyCons[+T](head: T, tail: MyList[T]) extends MyList[T]
这里MyCons的构造逻辑很明确:一个单个的T类型头部元素,加上一个MyList[T]类型的尾部列表。而::本质上就是MyCons的语法糖,自然只能接收单个元素当头部。
第二步:正确实现::操作符
如果你想让自定义List支持::这个语法,可以在trait里直接定义这个方法:
sealed trait MyList[+T] { // 支持协变的::方法,接收单个元素,返回新列表 def ::[U >: T](elem: U): MyList[U] = MyCons(elem, this) } case object MyNil extends MyList[Nothing] case class MyCons[+T](head: T, tail: MyList[T]) extends MyList[T]
现在调用的时候就得符合它的逻辑:比如1 :: MyNil,或者2 :: (1 :: MyNil)——都是把单个元素往头部加,而不是传整个列表。如果需要拼接两个完整的列表,那得实现:::(三个冒号)方法,比如:
sealed trait MyList[+T] { def ::[U >: T](elem: U): MyList[U] = MyCons(elem, this) // 实现列表拼接的:::方法 def :::[U >: T](other: MyList[U]): MyList[U] = other match { case MyNil => this case MyCons(h, t) => h :: (t ::: this) } }
第三步:伴生对象的apply方法该怎么写
apply方法的作用是让我们能用MyList(1,2,3)这种简洁的方式创建列表,所以它的参数必须是可变参数的T类型(也就是T*),能接收任意多个元素。具体实现可以这样:
object MyList { def apply[T](elems: T*): MyList[T] = { if (elems.isEmpty) MyNil else elems.head :: apply(elems.tail:_*) } }
这里利用可变参数接收一堆元素,然后递归地把每个元素往头部加,最终生成一个完整的列表。现在调用MyList(1,2,3)就等价于1 :: (2 :: (3 :: MyNil)),完全符合List的递归结构。
最后再划个重点
::方法的参数是单个T类型元素,用来往列表头部追加单个元素;- 拼接两个列表请用
:::方法,参数是另一个MyList[T]; - 伴生对象的
apply方法用可变参数T*,支持快速传入多个元素创建列表。
内容的提问来源于stack exchange,提问作者Aavik
相关产品推荐
相关产品推荐

