vumaasha
vumaasha

Reputation: 2845

How to write a bounded Generic Class in scala

I need to write a generic class that can only allow the types Long,Double in place of [V]

class DummyGenericClass[V](data:Seq[V])

Also, there will be an implementation difference based on the type. I want to do something like

val typ = if (V is Long) "x" else "y"

What is the recommended/best practice in scala to write such code?

Upvotes: 0

Views: 86

Answers (1)

adamwy
adamwy

Reputation: 1239

You can achieve it using sealed trait, which limits possible implementations to those that are defined in the same file:

sealed trait DummyGenericClass[V] {
  val data: Seq[V]
  val typ: String
}

class LongClass(val data: Seq[Long]) extends DummyGenericClass[Long] {
  val typ = "x"
}

class DoubleClass(val data: Seq[Double]) extends DummyGenericClass[Double] {
  val typ = "y"
}

If you want to have a generic constructor for DummyGenericClass the type safe way to do it is to use typeclass combined with factory pattern:

object DummyGenericClass {
  trait Factory[T] {
    def create(seq: Seq[T]): DummyGenericClass[T]
  }

  object Implicits {
    implicit val longProvider = 
      new Factory[Long] {
        def create(seq: Seq[Long]) = new LongClass(seq)
      }

    implicit val doubleProvider =
      new Factory[Double] {
        def create(seq: Seq[Double]) = new DoubleClass(seq)
      }
  }

  def apply[T: Factory](seq: Seq[T]) = implicitly[Factory[T]].create(seq)
}

Which you can use the following way:

import DummyGenericClass.Implicits._

val foo = DummyGenericClass(Seq.empty[Long])
val bar = DummyGenericClass(Seq.empty[Double])

// Won't compile:
val baz = DummyGenericClass(Seq.empty[String])

The other way, that doesn't require defining a typeclass to create DummyGenericClass instances, is to pass ClassTag bound to T in the factory method. However this solution is not recommended since it's not a type safe way, as it allows one to pass type argument that is not supported and will fail at runtime.

If you want to have a generic method that uses this constructor, you have to add DummyGenericClass.Factory context bound to that method too:

def add[T: DummyGenericClass.Factory]
    (a: DummyGenericClass[T], b: DummyGenericClass[T]) =
  DummyGenericClass(a.data ++ b.data)

import DummyGenericClass.Implicits._

add(DummyGenericClass(Seq(1.0)), DummyGenericClass(Seq(2.0)))

Upvotes: 3

Related Questions