Wednesday, September 23, 2009

BeanProperties

In Java-land many frameworks depend on Bean style properties. Scala has an extremely handy annotation that assists in interoperating with frameworks that require bean-style interfaces. In Scala 2.7 there are only a few annotations for helping:
  • @BeanProperty - Generates getters and setters for a property. (Or just a getter if the bean is a val)
  • @BeanInfo - Generates the associated BeanInfo for the class

Remember that if you want frameworks to be able to create the beans you need to make a 0-arg constructor and have the bean properties as fields.

Example with bean-properties as constructor arguments. This example requires Scala 2.8 because it uses BooleanBeanProperty. You can remove that and run with Scala 2.7.x.
In this example we define a bean class and use reflection to show that all the getters and setters are generated:

  1. scala> import scala.reflect._
  2. import scala.reflect._    
  3. scala> caseclass MyBean(
  4.      | @BeanPropertyvar mutable:String,  // getter and setter should be generated
  5.      | @BooleanBeanPropertyvar boolProp:Boolean, // is getter and normal setter should be generated
  6.      | @BeanPropertyval readOnly:Int) // only a getter should be generated
  7. defined class MyBean
  8. scala> val beanClass = classOf[MyBean]
  9. beanClass: java.lang.Class[MyBean] = class MyBean
  10. scala> val beanMethods = beanClass.getDeclaredMethods.filter( _.getName.matches("(get|is|set).+"))
  11. beanMethods: Array[java.lang.reflect.Method] = Array(public java.lang.String MyBean.getMutable(), public boolean MyBean.isBoolProp(), public int MyBean.getReadOnly(), public void MyBean.setBoolProp(boolean\
  12. ), public void MyBean.setMutable(java.lang.String))
  13. scala> println( beanMethods map (_.getName) mkString("\n"))
  14. getMutable
  15. isBoolProp
  16. getReadOnly
  17. setBoolProp
  18. setMutable


This next example would not run in the REPL for some reason so you must copy-paste it into a file, compile and run. Instructions:
  1. copy and paste code into bean.scala
  2. scalac bean.scala
  3. scala -cp . MyBean

  1. import scala.reflect._
  2. class MyBean{
  3.   @BeanProperty
  4.   var mutable:String = ""
  5. }
  6. object MyBean{
  7.   def apply(mutable:String) = {
  8.     val bean = new MyBean()
  9.     bean.mutable = mutable
  10.     bean
  11.   }
  12.   def main(args:Array[String]){
  13.     import java.beans._
  14.     val result = new java.io.ByteArrayOutputStream
  15.     val encoder = new XMLEncoder(result)
  16.     val bean = MyBean("hello")
  17.     encoder.writeObject( bean )
  18.     encoder.close
  19.     println(result.toString)
  20.   }
  21. }

1 comment:

  1. Great article that gave me exactly what I was looking for, as a scala newbie.

    I simplified the last example code a bit. Posting it for general benefit:

    import scala.reflect._

    class MyBean1{
    @BeanProperty
    var mutable:String = ""
    }

    object Runner{
    def main(args:Array[String]){

    import java.beans._

    val result = new java.io.ByteArrayOutputStream
    val encoder = new XMLEncoder(result)
    val bean = new MyBean1
    bean.mutable = "hello"
    encoder.writeObject( bean )
    encoder.close
    println(result.toString)
    }
    }

    Secondly, to run it from scala cmd line interpreter, ignore the Runner class and just post all the lines in its main method directly onto the interpreter.

    Hope this helps.

    /Ashish

    ReplyDelete