Groovy uses the Java package protected scope visibility rules to turn methods and classes into public scoped variants and fields into properties. In Java package scope visibility is applied to any field, method or class that has no explicit scope set, like public
, protected
or private
. To get the same scope in Groovy we must use the AST transformation @PackageScope
to explicitly define a field, method or class to have package scope.
In the following code we want to define a simple User
class with an accessor method to change the username
property. Only classes in the same package as the User
class must be allowed to use this method. The method may not be invoked by classes in other packages or subclasses.
package com.mrhaki.groovy.domain import groovy.transform.* class User { private String username User(final String username) { this.username = username } @PackageScope void changeUsername(final String newUsername) { this.username = newUsername } String getUsername() { username } }
If we create a new class in another package and we want to invoke the method changeUsername()
we get an error if we use @CompileStatic
. We can also write a Java class to use our Groovy User
class and then the compiler will also give an error.
// File: App.groovy package com.mrhaki.groovy.app import groovy.transform.* import com.mrhaki.groovy.domain.User @CompileStatic def changeUsername() { final User user = new User('mrhaki') user.changeUsername 'Hubert A. Klein Ikkink' } changeUsername()
If we run the script with Groovy we get an IllegalAccessError
:
$ groovyc User.groovy $ groovy App.groovy Caught: java.lang.IllegalAccessError: tried to access method com.mrhaki.groovy.domain.User.changeUsername(Ljava/lang/String;)V from class com.mrhaki.groovy.app.App java.lang.IllegalAccessError: tried to access method com.mrhaki.groovy.domain.User.changeUsername(Ljava/lang/String;)V from class com.mrhaki.groovy.app.App at com.mrhaki.groovy.app.App.changeUser(App.groovy:10) at com.mrhaki.groovy.app.App.run(App.groovy:13)
If we write a Java class to invoke the package scoped method we get an error when we compile the class:
package com.mrhaki.groovy.app; import com.mrhaki.groovy.domain.User; public class Application { public static void main(final String[] args) { final User user = new User("mrhaki"); user.changeUsername("Hubert A. Klein Ikkink"); } }
The compilation error:
$ javac -cp . Application.java Application.java:8: error: changeUsername(String) is not public in User; cannot be accessed from outside package user.changeUsername("Hubert A. Klein Ikkink"); ^ 1 error
The @PackageScope
annotation can be used at class, field and method level. We can also add an argument to the annotation if applied at class level. We can then use the constants in groovy.transform.PackageScopeTarget
to make all fields package scoped with FIELDS
, all methods with METHODS
and the class with CLASS
.
Code written with Groovy 2.2.1.