Scala copy of case class with dynamic parameter name

For the scala case class with the number of parameters (21 !!)

eg. case class Car(type: String, brand: String, door: Int ....) where type = jeep, brand = toyota, door = 4 .... etc

And there is a copy method that allows you to override using a named parameter: Car.copy(brand = Kia) where it becomes type = jeep, brand = Kia, door = 2 ... etc.

My question is, is there anyway I can provide a 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 } 

Can the scala reflection library provide help?

The reason I use the copy method is because I don’t want to repeat the 21 parameter assignments every time I create a case class that has only one or two parameters.

Thank you very much!

+2
source share
4 answers

FWIW, I just implemented a reflection version of Java: CaseClassCopy.scala . I tried TypeTag version , but it was not so 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] } } 
+3
source

Cannot use case classes.

The copy method generated at compile time and named parameters processed at compile time. There is no way to execute this runtime.

Dynamic can help solve your problem: http://hacking-scala.tumblr.com/post/49051516694/introduction-to-type-dynamic

0
source

Yes, for this you need to use reflection.

This is a bit related to the fact that copy is a synthetic method, and you will need to call getters for all fields except the ones you want to replace.

To give you an idea, the method in this class does just that, except for using the argument index instead of the name. It calls the comanion object apply method, but the effect is the same.

0
source

I'm a little confused - how is that not what you need?

 car: Car = ... // Retrieve an instance of Car somehow. car.copy(type = "jeep") // Copied instance, only the type has been changed. car.copy(door = 4) // Copied instance, only the number of doors has changed. // ... 

Is it because you have many options to create the source instance? In this case, you cannot use the default values?

 case class Car(type: String = "Jeep", door: Int = 4, ...) 

You seem to know about all these functions and feel that they do not fit your needs - could you explain why?

0
source

Source: https://habr.com/ru/post/1259013/


All Articles