I have two case classes
abstract class MainClass
case class Acc(x:String, y:String) extends MainClass
case class Bcc(x:String, y:String) extends MainClass
I need to write a generic fucntion that will take either one of them and return a list of the same case class, something similar to :
def f1[T <: MainClass ]():List[T]={
val o1 = new Acc("sss","ddd").asInstanceOf[T]
List(o1)
}
the only problem is that I am hard-coding the type class "Acc" . My question is How do I make the f1 method instantiate the generic class T instead of Acc or Bcc
thanks in advance.
While the ClassTag solution may work for you, relying on runtime reflection is a code smell, and it can actually get you into real trouble. Suppose you end up with a new subclass like this (defined either by you or by someone else using your code):
case class Ccc(x: String, y: String, i: Int) extends MainClass
Now when someone writes f1[Ccc]—and there's no indication in the method signature that they shouldn't—their program will crash at runtime with a NoSuchMethodException.
There's a much safer way to do this kind of thing, and while it requires a bit of boilerplate-writing for you, it's transparent to people using your code down the line (including yourself). First you define a type class that describes how to create an instance of some specific subclass of MainClass:
trait MainClassFromStrings[T <: MainClass] {
def apply(x: String, y: String): T
}
Then you write some type class instances:
implicit def AccFromStrings: MainClassFromStrings[Acc] =
new MainClassFromStrings[Acc] {
def apply(x: String, y: String) = Acc(x, y)
}
implicit def BccFromStrings: MainClassFromStrings[Bcc] =
new MainClassFromStrings[Bcc] {
def apply(x: String, y: String) = Bcc(x, y)
}
And now you can write your f1 pretty cleanly:
def f1[T <: MainClass](implicit fs: MainClassFromStrings[T]) =
List(fs("sss", "ddd"))
And then:
scala> f1[Acc]
res0: List[Acc] = List(Acc(sss,ddd))
scala> f1[Bcc]
res1: List[Bcc] = List(Bcc(sss,ddd))
But you get a nice compile-time error if you try f1[Ccc].
Type classes are pretty widely used in Scala (including in the standard library)—searching Stack Overflow for "type classes" in Scala will turn up lots of examples and discussion.
You can use ClassTag to keep meta information about Class during runtime.
import scala.reflect._
abstract class MainClass
case class Acc(x:String, y:String) extends MainClass
case class Bcc(x:String, y:String) extends MainClass
object Boot extends App {
def f1[T <: MainClass : ClassTag ]():List[T]={
val o1 = classTag[T].runtimeClass.getConstructor(classOf[String], classOf[String]).newInstance("sss","ddd").asInstanceOf[T]
List(o1)
}
val list = f1[Acc]
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With