Friday, January 23, 2015

Java vs Groovy : Access Modifiers

Although Groovy shares much of the same syntax as Java and Groovy scripts compile down to Java class files (see IBM's Fluenty Groovy), the two languages are not exactly the same. (see Groovy's Differences from Java) This fact became even more evident to me when I did my little access modifier experiment using Groovy. If you'll recall from that post I wrote a few simple classes in order to demonstrate how access modifiers behave in Java. The following table contains the results from that post. The cells marked with 'J' indicate when I was able to access the field and method.

public protected (no modifier) private
Subclass, Same Package J J J
Non-Subclass, Same Package J J J
Subclass, Different Package J J
Non-Subclass, Different Package J

When I did the same thing in Groovy, however, I got much different results. Note I've marked the cells with 'G' this time.

public protected (no modifier) private
Subclass, Same Package G G G throws exception
Non-Subclass, Same Package G G G G
Subclass, Different Package G G G throws exception
Non-Subclass, Different Package G G G G

One of the things you'll notice that's different from Java is that pretty much every class can access everything in Groovy. The only exception to this rule seems to be when trying to access a parent's private field or method from a subclass. Unfortunately, though, I still don't understand exactly why its like that.

At first I thought this was a major bug in Groovy that I had somehow stumbled across. I even wrote a JIRA ticket issue on it. (see GROOVY-7246) That's when Jochen Theodorou, who goes by the username blackdrag, kindly reminded me that Groovy is not Java and that I can get most of the behavior that I'm looking for if I put the @CompileStatic annotation on everything but the Earth.Earthling class. For example
package Earth;

import groovy.transform.CompileStatic;

// subclass, same package
@CompileStatic
public class Man extends Earthling {
    public Man() {
        System.out.println("name = " + name);
        speak();
    }

    public static void main(String[] args) {
        new Man();
    }
}
The following is the result when I do that:

public protected (no modifier) private
Subclass, Same Package O O O
Non-Subclass, Same Package O O O
Subclass, Different Package O O O
Non-Subclass, Different Package O
O

You'll notice that I can still access fields and methods with package access in classes outside of the package that they are defined in. I can fix most of this issue if I also use the @PackageScope annotation. Again, this was pointed out to me by Jochen. For example:
package Earth;

import groovy.transform.PackageScope;

// access modifiers will be changed for this class
public class Earthling {
    @PackageScope
    String name = "Bob";

    @PackageScope
    void speak() {
        System.out.println("Hello.");
    }
}
Here are the results from that

Can access field? Can access method ? Java
Subclass, Same Package
B B
Non-Subclass, Same Package B B B
Subclass, Different Package


Non-Subclass, Different Package B


As you can see there are still some “problems” particularly when it comes to accessing fields with package access. Speaking of fields, another oddity I came across occurred when I was looking at the interoperability of Groovy and Java classes. Specifically I created a Groovy class that had both a field and method that exercised all four access modifiers.
package stuff;

// GKlass.groovy
public class GKlass {

    public void PublicAccessMethod() {
    }

    protected void ProtectedAccessMethod() {
    }

    void PackageAccessMethod() {
    }

    private void PrivateAccessMethod() {
    }

    public String PublicAccessField;
    protected String ProtectedAccessField;
    String PackageAccessField;
    private String PrivateAccessField;
}
And then I created a Java class that tried to access those members:
package stuff;

// Klass.java
public class Klass {
   public  static void main(String... args) {
      GKlass klass = new GKlass();

      klass.PublicAccessMethod();
      klass.ProtectedAccessMethod();
      klass.PackageAccessMethod();
      //klass.PrivateAccessMethod();

      klass.PublicAccessField = "";
      klass.ProtectedAccessField = "";
      //klass.PackageAccessField = ""; // has private access
      //klass.PrivateAccessField = "";
   }
}
What's strange here is that Java complains when I try to access the field with package access.

I'm going to end this post with a few quotes I found in the documentation for Groovy.

"By default, Groovy automatically turns package protected fields into properties and makes package protected methods and classes public." (taken from API docs for @PackageScope)

"Currently Groovy doesn't distinguish properly between public, private, and protected members.." (taken from Groovy's "Things you can do but better leave undone")

No comments:

Post a Comment