Transform an object into an object.
Transform an object into an object. The class name will be generated,
will be in the org.clapper.classutil
package, and will have
a class name prefix of ScalaObjectToBean_
.
the Scala object
the desired class name
true
to recursively map nested maps, false
otherwise. Recursively mapped maps will have generated
class names.
an instantiated object representing the map
Transform an object into an object.
Transform an object into an object. The class name will be generated,
will be in the org.clapper.classutil
package, and will have
a class name prefix of ScalaObjectBean_
.
the Scala object
true
to recursively map nested maps, false
otherwise
an instantiated object representing the map
Transform an object into an object.
Transform an object into an object. The class name will be generated,
will be in the org.clapper.classutil
package, and will have
a class name prefix of ScalaObjectToBean_
. This version of the call
allows you to intercept each method call and possibly transform the
results.
Note that the cb
parameter will be receiving an AnyRef
result
(i.e., java.lang.Object
). That means any Scala primitives are really
boxed Java values. In addition, it's important that the method return an
object. For instance, consider wrapping the following class in a bean:
case class Foo(x: Int, y: Double)
To post-process a bean generated from that class, you might use code like the following:
val bean = ScalaObjectToBean.withResultMapper(Foo(10, 20.0)) { (name, res) => (name, res) match { case ("getX", n: java.lang.Integer) => new Integer(n * 100) case ("getY", d: java.lang.Double) => new java.lang.Double(d / 10.0) case _ => res } }
By contrast, the following will not compile, because (a) it attempts to
pattern match against Int
, and (b) it attempts to return a Scala
Double
.
val bean = ScalaObjectToBean.withResultMapper(Foo(10, 20.0)) { (name, res) => (name, res) match { case ("getX", n: Int) => new Integer(n * 100) // ^ pattern type is incompatible with expected type case ("getY", d: java.lang.Double) => d / 10.0 // ^ the result type of an // implicit conversaion must // be more specific than AnyRef case _ => res } }
Note, too, that if recurse
is true
(the default), the result value
passed to your post-call function will be a java.lang.reflect.Proxy
for
any non-primitive, non-String object.
case class Foo(i: Int) case class Bar(name: String, foo: Foo) val bean = ScalaObjectToBean(Bar("quux", Foo(10)) { (name, res) => (name, res) match { case ("getName", n) => // n will be a String case ("getFoo", f) => // f will be a java.lang.reflect.Proxy case ("name", n) => // n will be a String case ("foo", f) => // f will be a java.lang.reflect.Proxy } }
the Scala object
the desired class name, or an empty string to generate a default. (Empty string is the default.)
true
to recursively map nested maps, false
otherwise. Recursively mapped maps will have generated
class names. Defaults to false
.
If defined, this parameter is a function that is invoked with the result of any method call, along with the called method's name. It can be used to transform the result before the result is returned.
an instantiated object representing the map
ScalaObjectToBean
contains functions that allow you to map a Scala object into a read-only Java bean or merely generate an interface for such a bean. The functions take a Scala object or class (depending), locate the Scala accessors (using simple heuristics defined in ClassUtil), and generate a new interface or object with additional Java Bean getand
setmethods for the accessors.
This kind of wrapping is an alternative to using the
@BeanProperty
annotation on classes, so it is useful for mapping case classes into Java Beans, or for mapping classes from other APIs into Java Beans without having to extend them.ScalaObjectToBean
uses the following heuristics to determine which fields to map.First, it recognizes that any Scala
val
orvar
is really a getter method returning some type. That is:is compiled down to the equivalent of the following Java code:
So, the mapper looks for Scala getter methods that take no parameters and return some non-void (i.e., non-
Unit
) value, and it looks for Scala setter methods that take one parameter, return void (Unit
) and have names ending in "_$eq". Then, from that set of methods, the mapper discards:- Methods starting with "get" - Methods that have a corresponding "get" method. In the above example, if there's a
getX()
method that returns anint
, the mapper will assume that it's the bean version ofx()
, and it will ignorex()
. - Methods that aren't public. - Any method injava.lang.Object
. - Any method inscala.Product
.If there are any methods in the remaining set, then the mapper returns a new wrapper object that contains Java Bean versions of those methods; otherwise, the mapper returns the original Scala object. The resulting bean delegates its calls to the original object, instead of capturing the object's method values at the time the bean is called. That way, if the underlying Scala object's methods return different values for each call, the bean will reflect those changes.