I think it's not possible to make everything immutable because somewhere you need to store a changing system state (usually, in the model). Let me explain with some example. Sorry for the lengthy reply, I'm trying to be as clear as possible as this is a complex issue.
I assume that you want to follow MVC (model view controller) paradigm for your GUI. Your example code is not so clear so I will rewrite it to proper MVC.
In MVC, the V (view) listens to changes in the model M and updates the display accordingly.
In general, a listenable like M contains a Set (eg. the View V and Controller C). M must remain the same object in memory because it's shared by several users (V and C). It has to contain a "addListener" function that changes the Set in the original object. If it would only create a modified local copy of M, addListener would add the listener only for the object that calls addListener, but not the original that is used by the objects that want to call the added listener. Therefore a listenable in general can not be immutable.
Let me make this more specific, if this was too dense.
Your code is not proper MVC so I modify it to this to introduce the Model M.
public class Model {
final?? boolean clicked = false;
final?? Set<Listeners> listeners
void addChangeListener(l) {
listeners.add(l);
}
void doClick() {
clicked = true; // can not be final ?
listeners.forEach(l -> l.notifyChange(..event..));
}
boolean getClickedState() {
return clicked; // makes no sense if final ?
}
}
public final class ImmutableWindow {
final Model model;
public ImmutableWindow(Model m) {
this.model = m;
this.model.addChangeListener( evt ->
updatePanel(this.model.getClickedState() );
}
}
public final class ImmutableButton {
final Model model;
public ImmutableButton(Model m) {
this.model = m
}
protected void onClick() {
this.model.doClick();
}
}
The Model extends the listener pattern. Now it even has 2 properties that require it to be mutable
1. The Model must reflect the actual state of the system.
2. The Model is observable, so it contains a mutable set of listeners.
@1. m.doClick and m.getClickedState() would work (change Model/system state) only if Model is mutable. The field Model.clicked can't be final.
@2. listenable can't be immutable,as discussed above.
Now notice that ImmutableButton and ImmutableWindow are not really immutable anymore, because Model will turn out to be mutable. The "final" annotations will still work though because final only refers to the object reference , not to the contents of the referred object.
Alternatively, you might propose to make Model immutable and let it just forward all changes of Model to its listeners. But then ImmutableButton would have to store updates of the model. But if it would, then ImmutableButton.model can not be final and the ImmutableButton object would not be immutable...
In the end, someone has to store the changed objects. In MVC it's in the model, but whatever you do, the object that stores the change will be immutable.
I think the idea above with the event bus tries to hide the model behind some interface. The idea being that the interface can be the same fixed (or even static) object that it can point to some mutable data. But it does not work, as any object pointing to some mutable object also is mutable. Specifically, any object containing a mutable eventBus is mutable.
More radically you might try to remove the whole event and Listeners. But I don't see how to do the event handling without the shared listener system.
The best solution I have is to minimize the mutability of the model. Put only a SINGLE mutable object in the model, like SystemState, and mutate only that.