For scala case class with number of parameters (21!!)
e.g. case class Car(type: String, brand: String, door: Int ....)
where type = jeep, brand = toyota, door = 4 ....etc
And there is a copy method which allow override with named parameter: Car.copy(brand = Kia)
where would become type = jeep, brand = Kia, door = 2...etc
My question is, is there anyway I can provide the named parameter dynamically?
def copyCar(key: String, name: String) = {
Car.copy("key" = "name") // this is something I make up and want to see if would work
}
Is scala reflection library could provide a help here?
The reason I am using copy method is that I don't want to repeat the 21 parameters assignment every time when I create a case class which only have 1 or 2 parameter changed.
Many Thanks!
FWIW, I've just implemented a Java reflection version: CaseClassCopy.scala. I tried a TypeTag version but it wasn't that useful; TypeTag was too restrictive for this purpose.
def copy(o: AnyRef, vals: (String, Any)*) = {
val copier = new Copier(o.getClass)
copier(o, vals: _*)
}
/**
* Utility class for providing copying of a designated case class with minimal overhead.
*/
class Copier(cls: Class[_]) {
private val ctor = cls.getConstructors.apply(0)
private val getters = cls.getDeclaredFields
.filter {
f =>
val m = f.getModifiers
Modifier.isPrivate(m) && Modifier.isFinal(m) && !Modifier.isStatic(m)
}
.take(ctor.getParameterTypes.size)
.map(f => cls.getMethod(f.getName))
/**
* A reflective, non-generic version of case class copying.
*/
def apply[T](o: T, vals: (String, Any)*): T = {
val byIx = vals.map {
case (name, value) =>
val ix = getters.indexWhere(_.getName == name)
if (ix < 0) throw new IllegalArgumentException("Unknown field: " + name)
(ix, value.asInstanceOf[Object])
}.toMap
val args = (0 until getters.size).map {
i =>
byIx.get(i)
.getOrElse(getters(i).invoke(o))
}
ctor.newInstance(args: _*).asInstanceOf[T]
}
}
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