Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

scala - How to create a wrapper of List with a specific type

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]
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...