Java Package Bridge Pattern
Sun, Jan 13, 2019 (latest change)
The Java Package Bridge allows access between your packages without giving
access to the outside world. This makes it similar to the internal
keyword in the .NET framework, and slightly related with C++’s friend
.
WARNING
The Java Package Bridge described here may no longer be used for access between modules when the Java module system introduced in Java 9 is used. It is inherently incompatible because it requires classes in the same package in both modules which is explicitly forbidden. You can still use it inside a module for protected access, but now there are usually better ways then this.
Following is the original text of this post:
Rational
This is a pattern mostly useful for library designers. It allows you to define access paths between packages by extending package private access to another package of your choice.
How it works
The ordering of privacy rules of Java always seemed wrong to me:
private
access gives access for all classes in the same ‘.java’ file.- package protected (i.e. no keyword) allows access for all classes in the same package
protected
access allows access for extending classes and all classes in the same package.public
access allows access from everywhere.
I always felt that protected
should not give access for classes in the same
package. But now we will make use of exactly that strangeness. For
the bridge we need two support points, one in the accessing package and one
in the accessed package. This makes the bridge one-way, but of course you
can define another bridge for the back direction.
Support point in accessing package: the bridge interface
In the accessing package add a public abstract
class with
protected abstract
methods. You’ll need one method for each distinct feature
you want to access. But you can easily add more methods later if necessary.
We’ll call this class the bridge interface.
Support point in accessed package: the bridge implementation return
In the accessed package add a public return point for an object implementing the bridge interface.
In most cases this bridge implementation return can be an anonymous class.
Depending on your access parameters it possibly might even be a static final
constant.
Example
Start situation
Class A
in package accessed
has package private features.
package accessed;
public class A
{
static final String TEXT;
void setupSomethingSpecial(int value);
}
Class B
in package accessing
cannot access them:
package accessing;
public class B
{
B()
{
A a = new A();
a.setupSomethingSpecial(42); // not possible!
String txt = A.TEXT; // not possible!
}
}
Add a bridge interface in package accessing
:
package accessing;
public abstract class Bridge
{
protected abstract String getText();
protected abstract void setupSomethingSpecial(A a, int value);
}
Implement it in package accessed
:
In our example we’ll just add a constant field to class A. So now it looks like:
package accessed;
public class A
{
public static final Bridge BRIDGE = new accessing.Bridge {
protected String getText()
{
return TEXT;
}
protected void setupSomethingSpecial(A a, int value)
{
a.setupSomethingSpecial(a, value);
}
};
static final String TEXT;
void setupSomethingSpecial(int foobar);
}
Use the bridge
In class B
you can now use the bridge for accessing A
’s features.
So B
now looks like:
package accessing;
public class B
{
B()
{
A a = new A();
A.BRIDGE.setupSomethingSpecal(a, 42);
String txt = A.BRIDGE.getText();
}
}
Conclusions
Having this bridge pattern available gives you the means to divide your packages without sacrifying access restrictions. Keep in mind that Java access restrictions are not absolute and can all be circumvented.