Scala自定义Seq类时,如何结合ClassTag重写SeqFactory?
我正在实现自定义的Scala Seq类MyList,参考相关代码后,想要给类型参数A附加ClassTag,但直接在SeqFactory的方法(比如from、empty、newBuilder)上添加ClassTag约束时,会因为方法签名和SeqFactory中的定义不一致而无法重写。想请教如何正确实现带ClassTag的自定义集合?
以下是我尝试的两段代码:
尝试1:在类上添加ClassTag约束
package com.tiqwab.example.step4 import scala.collection.mutable.ListBuffer import scala.collection.{immutable, mutable, SeqFactory, StrictOptimizedLinearSeqOps} import scala.reflect.ClassTag class MyList[+A: ClassTag] private (elems: List[A]) extends immutable.LinearSeq[A] with immutable.LinearSeqOps[A, MyList, MyList[A]] with StrictOptimizedLinearSeqOps[A, MyList, MyList[A]] { override def isEmpty: Boolean = elems.isEmpty override def head: A = elems.head override def tail: MyList[A] = new MyList(elems.tail) override def iterator: Iterator[A] = elems.iterator override def iterableFactory: SeqFactory[MyList] = MyList override def toString: String = s"MyList(${elems.mkString(",")})" } object MyList extends SeqFactory[MyList] { override def from[A](source: IterableOnce[A]): MyList[A] = newBuilder[A].addAll(source).result() override def empty[A]: MyList[A] = new MyList(List.empty) override def newBuilder[A]: mutable.Builder[A, MyList[A]] = new ListBuffer[A].mapResult(elems => new MyList(elems)) }
尝试2:在工厂方法上添加ClassTag约束
package com.tiqwab.example.step4 import scala.collection.mutable.ListBuffer import scala.collection.{immutable, mutable, SeqFactory, StrictOptimizedLinearSeqOps} import scala.reflect.ClassTag class MyList[+A] private (elems: List[A]) extends immutable.LinearSeq[A] with immutable.LinearSeqOps[A, MyList, MyList[A]] with StrictOptimizedLinearSeqOps[A, MyList, MyList[A]] { override def isEmpty: Boolean = elems.isEmpty override def head: A = elems.head override def tail: MyList[A] = new MyList(elems.tail) override def iterator: Iterator[A] = elems.iterator override def iterableFactory: SeqFactory[MyList] = MyList override def toString: String = s"MyList(${elems.mkString(",")})" } object MyList extends SeqFactory[MyList] { override def from[A: ClassTag](source: IterableOnce[A]): MyList[A] = newBuilder[A].addAll(source).result() override def empty[A: ClassTag]: MyList[A] = new MyList(List.empty) override def newBuilder[A: ClassTag]: mutable.Builder[A, MyList[A]] = new ListBuffer[A].mapResult(elems => new MyList(elems)) }
核心问题在于SeqFactory的方法签名并未包含ClassTag约束,直接添加会破坏重写规则。可以通过以下几种方式解决:
1. 仅在需要的方法上添加ClassTag约束
因为你的MyList底层用List存储元素,本身不需要ClassTag——ClassTag通常是在创建数组等需要运行时类型信息的场景才必需。如果只是某些操作(比如转数组)需要类型信息,无需全局加约束,仅在对应方法上添加即可:
class MyList[+A] private (elems: List[A]) extends immutable.LinearSeq[A] with immutable.LinearSeqOps[A, MyList, MyList[A]] with StrictOptimizedLinearSeqOps[A, MyList, MyList[A]] { // ... 原有方法 ... // 仅在需要ClassTag的方法上添加约束 def toArray[B >: A : ClassTag]: Array[B] = elems.toArray[B] }
这种方式既满足了特定操作的需求,又不会破坏SeqFactory的接口契约。
2. 自定义工厂特质绕过SeqFactory限制
如果必须强制创建MyList时就要求ClassTag,可以不直接继承SeqFactory,而是自定义包含ClassTag约束的工厂特质,同时保留标准集合接口的兼容:
import scala.collection.{immutable, mutable} import scala.reflect.ClassTag trait MyListFactory { def empty[A: ClassTag]: MyList[A] def from[A: ClassTag](source: IterableOnce[A]): MyList[A] def newBuilder[A: ClassTag]: mutable.Builder[A, MyList[A]] } class MyList[+A] private (elems: List[A]) extends immutable.LinearSeq[A] with immutable.LinearSeqOps[A, MyList, MyList[A]] with StrictOptimizedLinearSeqOps[A, MyList, MyList[A]] { // ... 原有方法 ... override def iterableFactory: MyListFactory = MyList } object MyList extends MyListFactory { override def empty[A: ClassTag]: MyList[A] = new MyList(List.empty) override def from[A: ClassTag](source: IterableOnce[A]): MyList[A] = newBuilder[A].addAll(source).result() override def newBuilder[A: ClassTag]: mutable.Builder[A, MyList[A]] = new mutable.ListBuffer[A].mapResult(new MyList(_)) }
这种方式牺牲了与SeqFactory的直接兼容性,但完全满足ClassTag的强制约束需求。
3. 重载方法兼容SeqFactory接口
如果既要保留SeqFactory继承,又要支持创建时带ClassTag,可以在工厂对象中重写无约束的标准方法,同时额外提供带ClassTag约束的重载方法:
object MyList extends SeqFactory[MyList] { // 重写SeqFactory的无约束方法,确保安全创建MyList override def from[A](source: IterableOnce[A]): MyList[A] = newBuilder[A].addAll(source).result() override def empty[A]: MyList[A] = new MyList(List.empty) override def newBuilder[A]: mutable.Builder[A, MyList[A]] = new mutable.ListBuffer[A].mapResult(new MyList(_)) // 额外提供带ClassTag约束的方法,供需要时调用 def fromWithCT[A: ClassTag](source: IterableOnce[A]): MyList[A] = from(source) def emptyWithCT[A: ClassTag]: MyList[A] = empty[A] def newBuilderWithCT[A: ClassTag]: mutable.Builder[A, MyList[A]] = newBuilder[A] }
这种方式兼容了标准集合接口,同时为需要ClassTag的场景提供了明确入口。
如果不是必须在创建集合时强制要求ClassTag,优先选择方案1;如果必须强制创建时带ClassTag,则选择方案2;如果需要兼容SeqFactory接口,可选择方案3。
内容的提问来源于stack exchange,提问作者iluvex

