Java Package Bridge Pattern

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:

  1. private access gives access for all classes in the same ‘.java’ file.
  2. package protected (i.e. no keyword) allows access for all classes in the same package
  3. protected access allows access for extending classes and all classes in the same package.
  4. 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.