Forwarding calls to underlying object in Scala
-
30-06-2021 - |
Question
I have the following class definition:
class Foo[T](iteratorThunk: () => Iterator[T]) {
def values = iteratorThunk()
}
And I would like Foo
to have all the methods that Iterator
exposes, while still returning objects of type Foo
. For instance, I would like to be able to do:
val a = new Foo({ () => List(1, 2, 3).toIterator })
val b = new Foo({ () => List(4, 5, 6).toIterator })
val c = a ++ b
And have c
equal to:
new Foo({ () => a.values ++ b.values })
I had a look at forwarders (IterableForwarder
, TraversableForwarder
, ...), but none seemed fit to forward methods to an Iterator
, and I would still like to keep Foo
as both the static and dynamic result type.
What is the best way to achieve this behavior, without defining every forwarding method?
Solution
I need the Foo class as it is, and not only as an Iterator because I need to be able to do more than one pass through its values.
Then you want either a Traverable
or an Iterable
. To have them work as you wish, you need to extend both them and TraversableLike
or IterableLike
, through which you'll specify the return type. And you'll need to provide both a Builder
and a CanBuildFrom
as well.
Here's a simple implementation:
import scala.collection.IterableLike
import scala.collection.mutable.LazyBuilder
class Foo[T](iteratorThunk: () => Iterator[T]) extends Iterable[T] with IterableLike[T, Foo[T]] {
def iterator = iteratorThunk()
override protected def newBuilder = new Foo.FooBuilder[T]
}
object Foo {
class FooBuilder[T] extends LazyBuilder[T, Foo[T]] {
def result(): Foo[T] = {
val coll = parts.toList.flatten
new Foo(() => coll.iterator)
}
}
}