noncom
noncom

Reputation: 4992

Type-parametrized class with default type parameter

For example, I have the following class hierarchy:

abstract class A {
  def a() {}
  def aa()
}

class B extends A {
  def aa() {}
  def b()
}

class C extends A {
  def aa() {}
  def c()
}

And then I want to create a class that can store a collection of any combination of these classes instances. It will be able to call common methods. And due to type parametrization it must provide the ability to call class-specific methods if parametrized with these classes during the creation:

object Group {

  def apply(as: Buffer[A]) = new Group[A](as)
  def apply[T <: A](as: Buffer[T]) = new Group[T](as)
}

class Group[T <: A](as: Buffer[T]) {

  def a() { as.map(_.a()) }
  def aa() { as.map(_.aa()) }

}

so the Group can be created with the default, most generic type parameter like this:

val groupA = Group(Buffer[A]())
groupA.a()  //ok
groupA.aa() //ok
groupA.b()  //error
groupA.c()  //error

and it can be created when parametrized explicitly with one of the descendants of A:

val groupB = Group[B](Buffer[B]())
groupB.a()  //ok
groupB.aa() //ok
groupB.b()  //ok
groupB.c()  //error

and if it is possible, I would like to somehow remove the unnecessary type specification of [B] when creating the group, since it can be extracted from the passed buffer type:

val groupB = Group(Buffer[B]())

What is the correct way of realization of this functionality? Is it possible? Maybe there is a better architectural decision to accomplish this?

UPDATE: the code here is pseudocode, I just don't know how to write what I want.

UPDATE 2: I guess that calling type-specific methods like b() or c() should be realized through mapping:

groupC.as.map(_.c())

which is only possible, if the type parametrization is correct. That gets closer to my idea, but the exact possible way of realization remains a mystery (aside from a bunch of usages of asInstanceOf things)..

Upvotes: 1

Views: 618

Answers (1)

ziggystar
ziggystar

Reputation: 28680

You can use implicit conversions to seemingly add methods to only those instances of Group which are parameterized with the correct type. This can be considered a selective version of the pimp my library pattern.

scala> abstract class A { def a() {}; def aa() {} }
defined class A

scala> class B extends A {def b() {}}
defined class B

scala> class C extends A {def c() {}}
defined class C

Now I define the Group class. Take note on the implicit conversion for Group[C] that adds the c() method. This conversion is not applicaple to e.g. a Group[A] value.

scala> :paste
// Entering paste mode (ctrl-D to finish)

case class Group[T <: A](as: Seq[T])
object Group{implicit def forC(gc: Group[C]) = new {def c() {gc.as.map(_.c())}}}

// Exiting paste mode, now interpreting.

defined class Group
defined module Group

Now let's see if it works.

scala> val groupC = Group(new C :: new C :: Nil)
groupC: Group[C] = Group(List(C@7f144c75, C@da7d681))

scala> groupC.c()  //works

scala> val groupB = Group(new B :: new B :: Nil)
groupB: Group[B] = Group(List(B@e036122, B@7fde065d))

scala> groupB.c()  //fails as expected
<console>:16: error: value c is not a member of Group[B]
              groupB.c()
                     ^

Upvotes: 2

Related Questions