How to create a wrapper of List with a specific type

。_饼干妹妹 提交于 2019-11-28 05:44:42

问题


I am trying to create a wrapper of List with a specific type (e.g. List[Int]) such that methods that take an implicit CanBuildFrom parameter return an instance of my wrapper instead of List.

One possible solution, which feels rather heavyweight, is:

import scala.collection._
import generic.{CanBuildFrom, SeqForwarder}
import mutable.{Builder, ListBuffer}

class MyList(list: List[Int]) extends immutable.LinearSeq[Int]
                                 with LinearSeqLike[Int, MyList]
                                 with SeqForwarder[Int] {
  override def newBuilder: Builder[Int, MyList] = MyList.newBuilder
  protected override def underlying = list
}

object MyList {
  def newBuilder: Builder[Int, MyList] =
    new ListBuffer[Int] mapResult(new MyList(_))

  implicit def canBuildFrom: CanBuildFrom[MyList, Int, MyList] = {
    new CanBuildFrom[MyList, Int, MyList] {
      def apply(from: MyList) = from.newBuilder
      def apply() = newBuilder
    }
  }
}

val l1 = new MyList(List(1,2,3))

println(l1.isInstanceOf[MyList])
println(l1.map(_ + 1).isInstanceOf[MyList])
println(l1.filter(_ == 2).isInstanceOf[MyList])

Is there a better/easier way to create such a wrapper or did I miss anything important in the implementation of MyList?

Edit: A follow-up question is: Can this whole wrapper logic be put into ListWrapper classes or traits so that the above MyList can be implemented like this:

class MyList extends ListWrapper[Int, MyList]

object MyList extends ListWrapperFactory[Int, MyList]

回答1:


As far I know from reading this article:

http://www.scala-lang.org/docu/files/collections-api/collections-impl.html

your solution is the simplest one, if you want filter/map/etc. to all return instances of MyList. newBuilder is needed for ops like filter, and the implicit CanBuildFrom for ops like map, which may change the collection type.

What you should maybe do in your CanBuildFrom is this:

def apply(from: MyList) = from.newBuilder // call it on `from'

which ensures that a map on a statically-typed MyList which actually has a dynamic type that is a subtype of MyList will reuse that same dynamic type.

Edit: seems like there is a little something missing, for this map returns an instance of List and not MyList:

val l1: LinearSeq[Int] = new MyList(List(1, 2, 3))
println(l1.map(_ + 1)) // prints List(2, 3, 4)

it looks like this is also the case with the RNA example taken from the linked article. If it has static type IndexedSeq[Base] instead of RNA, a map on it returns a vector.

Edit 2: looks like this is a more general problem, discussed in this question.




回答2:


Regarding my follow-up question how to mixin the wrapper logic via classes or traits, this is what I came up with:

import scala.collection._

trait ListWrapper[Elem, Repr <: ListWrapper[Elem, Repr]]
    extends immutable.LinearSeq[Elem]
    with LinearSeqLike[Elem, Repr]
    with generic.SeqForwarder[Elem] { self: Repr =>

  def wrapperCompanion: ListWrapperCompanion[Elem, Repr]

  override def newBuilder: mutable.Builder[Elem, Repr] =
    wrapperCompanion.newBuilder
}

trait ListWrapperCompanion[Elem, Repr <: ListWrapper[Elem, Repr]] {
  def apply(elems: TraversableOnce[Elem]): Repr

  def newBuilder: mutable.Builder[Elem, Repr] =
    new mutable.ListBuffer[Elem].mapResult(apply)

  def canBuildFromWrapper: generic.CanBuildFrom[Repr, Elem, Repr] = {
    new generic.CanBuildFrom[Repr, Elem, Repr] {
      def apply(from: Repr) = from.newBuilder
      def apply() = newBuilder
    }
  }
}

Now MyList can be implemented by:

class MyList(val underlying: List[Int]) extends ListWrapper[Int, MyList] {
  def wrapperCompanion = MyList
}

object MyList extends ListWrapperCompanion[Int, MyList] {
  def apply(elems: TraversableOnce[Int]) = new MyList(elems.toList)

  implicit def canBuildFrom = canBuildFromWrapper
}

This is definitely better than having all the boilerplate code in MyList's definition, but it is still a lot to write for making MyList just a wrapper for List.



来源:https://stackoverflow.com/questions/5659893/how-to-create-a-wrapper-of-list-with-a-specific-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!