Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

scala accepting generic classes

Tags:

generics

scala

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.

like image 647
CruncherBigData Avatar asked Mar 24 '26 19:03

CruncherBigData


2 Answers

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.

like image 136
Travis Brown Avatar answered Mar 27 '26 09:03

Travis Brown


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]

}
like image 29
grotrianster Avatar answered Mar 27 '26 08:03

grotrianster



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!