Method 1 (public and package private modifiers)

Tabs

=== Protected

public final class Protected {

private int value;
private ChangeListener listener;

/** * Only friends can create instances. */Protected() {
}

/** * Anyone can change value of the item. */public void setValue(int newValue) {
value = newValue;
ChangeListener l = listener;
if (l != null) {
l.stateChanged(new ChangeEvent(this));
}
}

/** * Anyone can get the value of the item. */public int getValue() {
return value;
}

/** * Only friends can listen to changes. */void addChangeListener(ChangeListener l) {
assert listener == null;
listener = l;
}
}

=== ProtectedUser

@Componentpublic class ProtectedUser {

public void test() {
var protectedObj = new Protected();
protectedObj.addChangeListener(e -> { });
}
}

Method 2 (Accessor Design)

Tabs

=== Protected

same as above

=== ProtectedAccessorImpl

@Componentfinal class ProtectedAccessorImpl extends ProtectedAccessor {

static {
ProtectedAccessor.setDefault(new ProtectedAccessorImpl());
}

protected Protected newItem() {
return new Protected();
}

protected void addChangeListener(Protected aProtected, ChangeListener l) {
aProtected.addChangeListener(l);
}
}

=== ProtectedAccessor

public abstract class ProtectedAccessor {

private static volatile ProtectedAccessor DEFAULT;

public static ProtectedAccessor getDefault() {
ProtectedAccessor a = DEFAULT;
if (a == null) {
throw new IllegalStateException(“Something is wrong: ” + a);
}
return a;
}

public static void setDefault(ProtectedAccessor protectedAccessor) {
if (DEFAULT != null) {
throw new IllegalStateException();
}
DEFAULT = protectedAccessor;
}

public ProtectedAccessor() {
}

protected abstract Protected newItem();

protected abstract void addChangeListener(Protected aProtected, ChangeListener l);
}

=== ProtectedUser

@Componentpublic class ProtectedUser {

public void test() {
var protectedObj = ProtectedAccessor.getDefault().newItem();
ProtectedAccessor.getDefault().addChangeListener(protectedObj, e -> { });
}
}

Resources