Home > front end >  Create a collection of case class names
Create a collection of case class names

Time:04-28

I'm working in Spark 3.1 with Scala 2.12.10.

I'd like to create a collection (Seq, whatever) of case classes that have implemented a common trait, so I can execute some generic dataset code on the collection members without having to type each incantation separately. I think I can get the erased type via TypeTag, but I'm unable to define the collection in the first place!

Given these types:

trait HasID { val id: String }

case class Customer(id: String, name: String) extends HasID
case class Product(id: String, desc: Int) extends HasID
case class Sale(id: String, value: Float) extends HasID 

class Appender[T <: HasID : Encoder] { ... } // dataset methods

I can call the code:

new Appender[Customer]() ...
new Appender[Product]() ...
new Appender[Sale]() ...

But what I want is to put the classes in a collection and loop through it:

Seq[HasID](Customer, Product, Sale).foreach(c =>
  // get type C of c somehow
  new Appender[C]() ...
)

CodePudding user response:

This:

case class Customer(id: String) extends HasId {...}

Is compiled into something like this (not completely equal):

class Customer(val id: String) extends HasId {...}
object Customer { def apply(id: String): Customer = ??? }

So pay attention that the object is not extending HasId. Companion objects kind of hold the static members of a class, but the id here is instance-dependent. Now if you just need the companion objects, and you can use reflection or something to access the id of an actual object, I recommend you to define a class and a companion object instead of a case class:


trait HasId {
  def id: String
}

trait ClassHasIdMarker

class Customer private(override val id: String) extends HasId
object Customer extends ClassHasIdMarker {
  def apply(id: String): Customer = new Customer(id)
}
// These 2 are companions
// all the companion objects now extend a single trait (you can create a seq now)
// if you use reflection to access objects of the class, you can have the id

Please let me know if I understood your question correctly and if this helps!


Update

A type class would also be a good idea (as @Luis Miguel Suarez mentioned)

trait ObjectCreaterWithId[ActualType <: HasId] {
  def apply(id: String): HasId
}
// class Customer ...
object Customer extends ObjectCreaterWithId[Customer] {
  override def apply(id: String): HasId = new Customer(id)
}

CodePudding user response:

Actually, it works if you change the type Seq[HasID] to Seq[String => HasID]. You don't need a ObjectCreaterWithId or HasIDMaker at all:

scala> val objs = Seq[String => HasID](Customer, Product, Sale)
val objs: Seq[String => HasID] = List(Customer, Product, Sale)

// And then you can create a list of instances by objs
scala> val ids = objs map (_ ("one"))
val ids: Seq[HasID] = List(Customer(one), Product(one), Sale(one))
  • Related