Create object at runtime which have the same internal contents

86 Views Asked by At

I am performing a repetitive task of creating a object that has same internal contents and hence thought of creating a generic method that would help me achieve this.

The internal object is as follows

case class Data(value: Int)

I have a base trait as follows

trait Base

There are a couple of classes that extend this trait

case class A(data: Data) extends Base
case class B(data: Data) extends Base
case class C(data: Data) extends Base

The generic method that I am trying to write to create the object

def getObject[T <: Base](data: Data, t: T) = {
  T(data)
}

However, while trying to do so, I get a compile-time error saying that

Cannot resolve symbol T

Can you please let me know what I am missing in this method implementation. Note:- This is a very simplistic implementation of what I am trying to do in my code.

2

There are 2 best solutions below

0
Mario Galic On BEST ANSWER

Consider typeclass solution for compile-time safety

final case class Data(value: Int)
final case class A(data: Data)
final case class B(data: Data)
final case class C(data: Data)

trait BaseFactory[T] {
  def apply(data: Data): T
}

object BaseFactory {
  def apply[T](data: Data)(implicit ev: BaseFactory[T]): T = ev.apply(data)
  implicit val aBaseFactory: BaseFactory[A] = (data: Data) => A(data)
  implicit val bBaseFactory: BaseFactory[B] = (data: Data) => B(data)
  implicit val cBaseFactory: BaseFactory[C] = (data: Data) => C(data)
}

val data = Data(42)
BaseFactory[A](data)   // res0: A = A(Data(42))
BaseFactory[B](data)   // res1: B = B(Data(42)) 
BaseFactory[C](data)   // res2: C = C(Data(42))
2
Krzysztof Atłasik On

Due to type-erasure, you can't use generic type to create a new instance of an object.

You could use ClassTag to capture a class of T at runtime:

case class Data(value: Int)

trait Base

case class A(data: Data) extends Base
case class B(data: Data) extends Base
case class C(data: Data) extends Base

def getObject[T <: Base](data: Data)(implicit ct: ClassTag[T]): T = 
  ct.runtimeClass.getDeclaredConstructors.head.newInstance(data).asInstanceOf[T]


val a: A = getObject[A](Data(1))
val b: B = getObject[B](Data(2))
val c: C = getObject[C](Data(3))

As cchantep noticed, it has a drawback, that if your case class doesn't have a constructor with single argument Data, this function will fail at runtime.