Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Instantiating a case class from a list of parameters

Tags:

scala

Given:

case class Foo(a: Int, b: String, c: Double) 

you can say:

val params = Foo(1, "bar", 3.14).productIterator.toList 

and get:

params: List[Any] = List(1, bar, 3.14) 

Is there a way to "go backwards" and recreate a Foo object directly from this list, i.e.:

Foo.createFromList(params)   // hypothetical 

instead of writing:

Foo(params(0).asInstanceOf[Int], params(1).asInstanceOf[String], params(2).asInstanceOf[Double]) 

EDIT: it seems that it boils down to being able to send the elements of a list as parameters to a function without writing them out explicitly, e.g.:

def bar(a: Int, b: Int, c: Int) = //... val list = List(1, 2, 3, 4, 5) bar(list.take(3)) // hypothetical, instead of: bar(list(0), list(1), list(2)) 

I would sort of expect to be able to do:

bar(list.take(3): _*) 

but that doesn't seem to work.

EDIT: Solution based on extempore's answer, but invoking the constructor directly instead of using the apply method:

case class Foo(a: Int = 0, b: String = "bar", c: Double = 3.14) {     val cs = this.getClass.getConstructors     def createFromList(params: List[Any]) =     cs(0).newInstance(params map { _.asInstanceOf[AnyRef] } : _*).asInstanceOf[Foo] } 

Now you can do:

scala> Foo().createFromList(List(4, "foo", 9.81)) res13: Foo = Foo(4,foo,9.81) 

You can also refactor the creation method into a trait:

trait Creatable[T <: Creatable[T]] {     val cs = this.getClass.getConstructors     def createFromList(params: List[Any]) =         cs(0).newInstance(params map { _.asInstanceOf[AnyRef] } : _*).asInstanceOf[T]    }  case class Bar(a: Int = 0, b: String = "bar", c: Double = 3.14) extends Creatable[Bar] 

And do e.g.:

scala> val bar = Bar() bar: Bar = Bar(0,bar,3.14)  scala> bar == bar.createFromList(bar.productIterator.toList) res11: Boolean = true 
like image 521
Knut Arne Vedaa Avatar asked Nov 27 '10 09:11

Knut Arne Vedaa


People also ask

What is difference between Case class and class in Scala?

A class can extend another class, whereas a case class can not extend another case class (because it would not be possible to correctly implement their equality).

Can Case classes contain member functions?

It can contain both state and functionality. Case classes are like data POJO in Java. classes that hold state, which can be used by functions (usually in other classes).

What is the difference between case class and case object?

Note: The Case class has a default apply () method which manages the construction of object. A Case Object is also like an object, which has more attributes than a regular Object. It is a blend of both case classes and object. A case object has some more features than a regular object.

What is a parameterized class in Java?

Parameters are like constants that are local to the specified class. Classes are allowed to have default value for each parameter that can be overridden during class instantiation. Given below is a parameterized class which has size as the parameter that can be changed during instantiation.

What is instantiating an object in Python?

Instantiating an object in Python consists of a few stages, but the beauty of it is that they are Pythonic in themselves - understanding the steps gives us a little bit more understanding of Python in general. Foois a class, but classes in Python are objects too!

What are the benefits of using case class in Scala?

It has a by default hashCode implementation. The one of the topmost benefit of Case Class is that Scala Compiler affix a method with the name of the class having identical number of parameters as defined in the class definition, because of that you can create objects of the Case Class even in the absence of the keyword new.


2 Answers

scala> case class Foo(a: Int, b: String, c: Double) defined class Foo  scala> val params = Foo(1, "bar", 3.14).productIterator.toList params: List[Any] = List(1, bar, 3.14)  scala> Foo.getClass.getMethods.find(x => x.getName == "apply" && x.isBridge).get.invoke(Foo, params map (_.asInstanceOf[AnyRef]): _*).asInstanceOf[Foo] res0: Foo = Foo(1,bar,3.14)  scala> Foo(1, "bar", 3.14) == res0 res1: Boolean = true 

Edit: by the way, the syntax so far only being danced around for supplying the tuple as an argument is:

scala> case class Foo(a: Int, b: String, c: Double) defined class Foo  scala> Foo.tupled((1, "bar", 3.14))                 res0: Foo = Foo(1,bar,3.14) 
like image 162
psp Avatar answered Sep 19 '22 03:09

psp


You could use pattern matching like:

params match {                                     case List(x:Int, y:String, d:Double) => Foo(x,y,d) } 
like image 38
bjartek Avatar answered Sep 21 '22 03:09

bjartek