Loading...

September 22, 2015

Groovy Goodness: Defining Public Accessible Constant Fields

There is a catch when we define a constant field in Groovy. Rob Fletcher blogged about this in the post Groovy the public keyword a while ago. When we omit the public keyword for a method then the method is still accessible as public method, because Groovy makes the method public when the class is compiled. When we leave out the public keyword for fields Groovy creates a getter and setter method for the field at compile time and turns it into a property that applies to the Java Bean specification. This is also true if the field is static. So if we define a constant value as static final we must keep in mind that Groovy will generate a getter method so the constant value is a read only property according to Java Bean specification.

Let's create a simple class with a constant field DEFAULT, a property message and a message method. We leave out any public keyword:

// File: sample.groovy
// Groovy makes class public.
class Sample {
    // Groovy adds getDEFAULT and no setDEFAULT.
    static final String DEFAULT = 'default'
  
    // Groovy adds setMessage/getMessage
    String message

    // Groovy makes method public.
    void message(final String newMessage) {
        this.message = message
    }
}

If we compile this class we get the following methods and fields (using javap to inspect the class):

$ javap -p -constants Sample
Compiled from "sample.groovy"
public class Sample implements groovy.lang.GroovyObject {
  private static final java.lang.String DEFAULT = "default";
  private java.lang.String message;
  ...
  public void message(java.lang.String);
  ...
  public static final java.lang.String getDEFAULT();
  public java.lang.String getMessage();
  public void setMessage(java.lang.String);
}

If we want to access the constant field in Groovy we can still use Sample.DEFAULT, but from Java code this doesn't work. You can see in the generated class file we should invoke getDEFAULT(), because this method is public. To overcome this we simply add public to our constant field definition. This way Groovy will leave the field unchanged and in the generated class file it is still public. Then from Java we can use Sample.DEFAULT to access the constant value. Let's see the output of javap when we make the DEFAULT field public:

$ javap -p -constants Sample
Compiled from "sample.groovy"
public class Sample implements groovy.lang.GroovyObject {
  public static final java.lang.String DEFAULT = "default";
  private java.lang.String message;
  ...
  public void message(java.lang.String);
  ...
  public java.lang.String getMessage();
  public void setMessage(java.lang.String);
}

This also helps an IDE, like IntelliJ IDEA, to do a proper import static based on the constant field.

Written with Groovy 2.4.4.