I am using Play Framework 2.1.5 in a Java application.
I've got a view component that takes a list of generic objects as a parameter. In this component, I want to iterate on the list and get some properties of each element.
This would look like something like this:
@(elements: List[_])
@for((element, i) <- elements.view.zipWithIndex) {
@i
@element.id
@element.name
}
(I need those 3 values)
But, of course, element.id
and element.name
would not compile even if the type of objects I put in the list contained these methods. So I did this:
@for((element, i) <- elements.view.zipWithIndex) {
@defining(
ViewsUtils.getGenericElementId(element),
ViewsUtils.getGenericElementName(element)) {
case (id, name, something) =>
@i
@id
@name
}
}
And in a Java utility class:
public final class ViewsUtils {
public static String getGenericElementId(Object object) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
return object.getClass().getMethod("getId").invoke(object).toString();
}
public static String getGenericElementName(Object object) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
return object.getClass().getMethod("getName").invoke(object).toString();
}
}
This works but I know it's not right because it could throw a RuntimeException in case one of these methods didn't exist for the type of object I put in the list.
Here are my suppositons:
Casting the elements
Using inheritance
As I just need 2 properties of each object (id and name), I could use a map, but I need the index of the loop (is it possible to get it from a map?) and it wouldn't work if I needed more than 2 properties.
Maybe Scala provides the syntax for this kind of stuff.
Or maybe I'm just looking in the wrong direction.
Thank you for your help.
Ok, that is a bit too much for a comment, so I'll risk to post it as an answer.
Assuming you wish to never get a RuntimeException
you mentioned it makes sense that your object implement some interface (or mix in a trait, as it is said in Scala), so you won't risk getting an exception and no more need reflection to get values.
Suppose, you declare such a trait:
trait GenericObject {
val id: Long
val name: String
}
Then you declare some case classes:
case class A(id: Long, name: String, someOtherField: SomeType) extends GenericObject {
//your implementation
}
case class B(id: Long, name: String) extends GenericObject
case class C(id: Long, name: String) extends B(id, name)
Now you can change your template like this:
@(elements: List[GenericObject])
@for((element, i) <- elements.view.zipWithIndex) {
@i
@element.id
@element.name
}
And you should pass the list of GenericObject
s to your template:
val myObjects: List[GenericObject] = List(A(1, "A name"), B(2, "B name"), C(3, "C name"))
Ok(your_template.scala.html(myObjects))