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))