Another useful technique to not expose too much in API is to give access to certain functionality (e. g. ability to instantiate a class or to call a certain method) just to a
friend
code.
Java by default restricts the friends of a class to those classes that are in the same package. If there is a functionality that you want share just among classes in the same package, use package-private modifier in definition of a constructor, a field or a method and then it will remain accessible only to friends
.
Sometimes however it is more useful to extend the set of friends to a wider range of classes - for example one wants to define a pure API package and put the implementation into separate one. In such cases following trick can be found useful. Imagine there is a class item:
public final class api.Item {
Item(int value) {
this.value = value;
}
public int getValue() {
return value;
}
final void addListener(Listener l) {
}
}
that is part of the API, but cannot be instanitated nor listened on outside of the friend classes (but these classes are not only in api package). Then one can define an
Accessor
in the non-API package:
public abstract class impl.Accessor {
public static Accessor DEFAULT;
static {
Class c = api.Item.class;
try {
Class.forName(c.getName(), true, c.getClassLoader());
} catch (ClassNotFoundException ex) {
assert false : ex;
}
assert DEFAULT != null : "The DEFAULT field must be initialized";
}
public abstract Item newItem(int value);
public abstract void addListener(Item item, Listener l);
}
with abstract methods to access all
friend
functionality of the
Item
class and with a static field to get the accessor's instance. The main trick is to implement the
Accessor
by a (non-public) class in the
api
package:
final class api.AccessorImpl extends impl.Accessor {
public Item newItem(int value) {
return new Item(value);
}
public void addListener(Item item, Listener l) {
return item.addListener(l);
}
}
and register it as the default instance first time somebody touches
api.Item
by adding a static initializer to the
Item
class:
public final class Item {
static {
impl.Accessor.DEFAULT = new api.AccessorImpl();
}
}
Then the
friend code can use the accessor to invoke the hidden functionality from any package:
api.Item item = impl.Accessor.DEFAULT.newItem(10);
impl.Accessor.DEFAULT.addListener(item, this);
版权所有 罗明