2017-09-09 91 views
4

考慮以下自定義Seq工作:如何在Scala中創建具有有界類型參數的自定義Seq?

class MySeq[B](val s: Seq[B]) 
extends Seq[B] 
with GenericTraversableTemplate[B, MySeq] 
with SeqLike[B, MySeq[B]] { 
    override def companion = MySeq 

    def iterator = s.iterator 

    def apply(i: Int) = s(i) 

    def length = s.length 

    override def toString = s map { _.toString } mkString("\n") 
} 
object MySeq extends SeqFactory[MySeq] { 
    implicit def canBuildFrom[B]: CanBuildFrom[Coll, B, MySeq[B]] = 
     new GenericCanBuildFrom[B] 
    def newBuilder[B] = new ListBuffer[B] mapResult (x => new MySeq(x.toSeq)) 
} 

我想強加約束的類型參數B。換句話說,我想這樣的事情(不工作):

class MyA 

class MySeq[+B <: MyA](val s: Seq[B]) 
extends Seq[B] 
with GenericTraversableTemplate[B, MySeq] 
with SeqLike[B, MySeq[B]] { 
    override def companion = MySeq // Type Mismatch Here 

    def iterator = s.iterator 

    def apply(i: Int) = s(i) 

    def length = s.length 

    override def toString = s map { _.toString } mkString("\n") 
} 
object MySeq extends SeqFactory[MySeq] { 
    implicit def canBuildFrom[B]: CanBuildFrom[Coll, B, MySeq[B]] = 
     new GenericCanBuildFrom[B] 
    // Type Mismatch in the line below 
    def newBuilder[B] = new ListBuffer[B] mapResult (x => new MySeq(x.toSeq)) 
} 

但我得到的指示線以下類型不匹配錯誤:

inferred type arguments [B] do not conform to 
class MySeq's type parameter bounds [+B <: MyA] 
Main.scala line 49 

type mismatch; 
found : countvotes.structures.MySeq.type 
required: scala.collection.generic.GenericCompanion[Seq]  
Main.scala line 36 

type mismatch; 
found : MySeq[B(in class MySeq)] 
required: MySeq[B(in method newBuilder)]  
Main.scala line 49 

type mismatch; 
found : scala.collection.immutable.Seq[B(in method newBuilder)] 
required: Seq[B(in class MySeq)]  
Main.scala line 49 

我曾試圖解決這個問題將邊界添加到CanBuildFrom和newBuilder的類型參數中,但隨後出現其他錯誤消息。

如何創建一個自定義Seq並綁定了一個類型參數?

+1

你而擴展集合然後在包裝類與上限要求封裝它的任何理由? –

+0

原因是它會更優雅。我會在這堂課上使用'map'和'filter',每次手動解包和重新包裝都會很不方便。 – Bruno

回答

2

我沒有收到上線26的錯誤:

override def companion = MySeq 

也許別的東西造成這一點。

無論如何,問題是你不能有GenericCompanion[MySeq](超類型SeqFactory)。原因是GenericCompanion[Coll]意味着您可以爲任何A構建Coll[A](請參閱newBuilder的簽名)。你也不能有MySeq[A] <: GenericTraversableTemplate[A, MySeq],因爲genericBuilder是不可能的。這是有道理的; MySeq實際上並不是一個「通用集合」,因爲它想要它的所有元素都是MyA

解決方案是有MySeq[B] <: GenericTraversableTemplate[B, Seq],(免費提供extends Seq)。然後你有兩個選擇companion。它可以是Seq中的默認值,也可以是s.companion。在第一種情況下,((as: MySeq[A]): Seq[A]).map(...)將產生一個List(在運行時;在編譯時它只是一個通用的Seq)。在第二個,它將取決於as.s是什麼(再次,在運行時;編譯時將只看到一個Seq)。不過,你可以保留extends SetLike

然後,您需要提供自定義CanBuildFromMySeq.canBuildFrom[A <: MyA]: CanBuildFrom[MySeq[A], A, MySeq[A]],並定義MySeq#newBuilder

class MySeq[+B <: MyA](val s: Seq[B]) 
    extends Seq[B] 
    with SeqLike[B, MySeq[B]] 
{ 
    override def iterator = s.iterator 
    override def apply(i: Int) = s(i) 
    override def length = s.length 

    override def toString = s.map(_.toString).mkString("\n") 

    override def companion = s.companion 
    protected[this] override def newBuilder: mutable.Builder[B, MySeq[B]] = new mutable.Builder[B, MySeq[B]] { 
    private[this] val base = s.genericBuilder[B] 
    override def +=(elem: B) = { base += elem; this } 
    override def clear() = base.clear() 
    override def result() = new MySeq[B](base.result()) 
    } 
} 

object MySeq { 
    implicit def canBuildFrom[A <: MyA]: CanBuildFrom[MySeq[_], A, MySeq[A]] = ??? 
} 

val list = List(new MyA, new MyA, new MyA, new MyA) 
val vect = list.toVector 
val mLst = new MySeq(list) 
val mVec = new MySeq(vect) 
{ 
    val res = mLst.filter(_.hashCode != list.head.hashCode) 
    implicitly[res.type <:< MySeq[MyA]] 
} 
{ 
    val res = (mVec: Seq[MyA]).map(identity) 
    assert(res.isInstanceOf[Vector[_]]) 
} 
{ 
    val res = (mLst: Seq[MyA]).map(identity) 
    assert(res.isInstanceOf[List[_]]) 
} 
0

這是基於HTNW的上述接受的答案全MWE,與canBuildFrom實現:

class MyA 

class MySeq[+A <: MyA](val s: Seq[A]) 
    extends Seq[A] 
    with SeqLike[A, MySeq[A]] 
{ 
    override def iterator = s.iterator 
    override def apply(i: Int) = s(i) 
    override def length = s.length 

    override def toString = s.map(_.toString).mkString("\n") 

    override def companion = s.companion 
    protected[this] override def newBuilder = MySeq.newBuilder 
} 

object MySeq { 
    def newBuilder[A <: MyA] = new mutable.Builder[A, MySeq[A]] { 
    private[this] val base = Seq.newBuilder[A] 
    override def +=(elem: A) = { base += elem; this } 
    override def clear() = base.clear() 
    override def result() = new MySeq[A](base.result()) 
    } 

    implicit def canBuildFrom[A <: MyA]: CanBuildFrom[MySeq[_], A, MySeq[A]] = new CanBuildFrom[MySeq[_], A, MySeq[A]] { 
    def apply(from: Election[_]) = newBuilder 
    def apply() = newBuilder 
    } 
} 
相關問題