Loading...

Tuesday, August 18, 2009

Groovy Goodness: GroovyBeans the Simpler JavaBeans

We use JavaBeans a lot in our day to day Java programming. GroovyBeans use a much simpler syntax to get the same result. If we define a property in a JavaBean we must provide the corresponding get/set methods. Most IDEs can generate these methods for us, but still the resulting code of a JavaBeans is less readable, because we have a lot of these methods.

Let's see some code first before we continue to look into GroovyBeans in more detail:

class Car {
 int numberOfDoors
 String model
 String brand
 boolean automatic
 double price
 
 String toString() {
     "[Car details => brand: '${brand}', model: '${model}', #doors: '${numberOfDoors}', automatic: '${automatic}', price: '${price}']"
 }
}

We can use the Car GroovyBean from a Groovy class or script:

Car ford = new Car(brand: 'Ford', model: 'Focus', numberOfDoors: 4, automatic: false, price: 24995)
Car toyota =  new Car(brand: 'Toyota', model: 'Verso')
toyota.automatic = true  // Yes, this invokes setAutomatic.
toyota.setPrice(28919)  // Normal set invocation.
toyota.setNumberOfDoors 5  // In Groovy we can omit the parentheses.
println ford  // Output: [Car details => brand: 'Ford', model: 'Focus', #doors: '4', automatic: 'false', price: '24995.0']
println toyota // Output: [Car details => brand: 'Toyota', model: 'Verso', #doors: '5', automatic: 'true', price: '28919.0']

The class Car is our GroovyBean. It looks like we defined only some fields, but because we haven't use any of the access modifiers (public, protected or private) these fields are actually properties when the code is compiled. Groovy will generate the get/set methods so we can use the properties (see a complete list of rules the compiler uses to generate properties). To prove this is true we write a simple Java application to use the Car class:

public class CarApp {
 public static void main(String[] args) {
     Car car = new Car();
  car.setNumberOfDoors(3);
  car.setModel("A3");
  car.setBrand("AUDI");
  car.setPrice(32010);
  System.out.println(car); // Output: [Car details => brand: 'AUDI', model: 'A3', #doors: '3', automatic: 'false', price: '32010.0']
 }
}

GroovyBeans are flexible. To make a property read-only when designate a field final. Let's make the brand final and run our Groovy script again. We now get the following exception:

Caught: groovy.lang.ReadOnlyPropertyException: Cannot set readonly property: brand for class: Car

We can add our own get/set methods if we like. For example in the following code we have added our own protected set method for the model property:

class Car {
 int numberOfDoors
 String model
 final String brand  // Readonly, public getter is generated, no setter.
 boolean automatic
 double price
 
 // Protected setter, public getter is still generated.
 protected void setModel(model) {
     this.model = modelName
    }
 
 String toString() {
     "[Car details => brand: '${brand}', model: '${model}', #doors: '${numberOfDoors}', automatic: '${automatic}', price: '${price}']"
 }
}

To access the fields in GroovyBeans directly we can use the .@ operator. With this operator we bypass the get/set methods. Of course we shouldn't be doing this, because that is why we have properties, but still the following code shows how it works:

class Car {
 int numberOfDoors
 String model
 String brand
 boolean automatic
 double price
 
 public void setBrand(brand) {
     this.brand = brand + ' (set via setter method)'
 }
 
 String toString() {
     "[Car details => brand: '${brand}', model: '${model}', #doors: '${numberOfDoors}', automatic: '${automatic}', price: '${price}']"
 }
}

Car ford = new Car(brand: 'Ford', model: 'Focus', numberOfDoors: 4, automatic: false, price: 24995)
println ford.brand  // Output: Ford (set via setter method)

ford.@brand = 'Overrule brand'
println ford.brand  // Output: Overrule brand
println ford.@brand  // Output: Overrule brand

GroovyBeans provide a clean way to define beans with properties usable in Groovy and Java code.

0 comments:

Post a Comment