The Single Inheritance Problem
Problems arise when application developers and third party framework developers extend JavaFX classes to add more features. As an example, an essential feature of my company's framework is internationalization, and to enable it across all of the user interface, I have extended each of the built in JavaFX controls; my internationalizable text field is called ITextField, and it extends TextField. Similarly, IComboBox extends ComboBox, IButton extends Button – you get the picture.
Since all of these classes have a number of common methods, they all implement a common interface called IControl. When an application developer sees a class implementing IControl, he will immediately be aware that a number of additional features are available.
Some parts of my framework add cross cutting features, and some of those features work only for IControls; consequently I have API methods similar to
public Something fooBar(IControl control) {…}Although this guarantees that the control parameter is internationalizable, there's a catch. Have you spotted it? Since the parameter is required to be an IControl there is no longer a compile time requirement that it is also a Control. Why? Well, since Control is a class – not an interface – there is no way I can make my IControl interface extend Control, to signal that only Controls should be IControls. So, if a developer has not yet realized this requirement, he could turn any class into an IControl by implementing the interface – it does not even have to be a scene node. Obviously, my framework will fail at runtime if the developer implements IControl from a non-control class; but there is no way to reveal the mistake at compile time.
As an additional nuisance, this problem also requires me to do a lot of casting internally in my framework code. Everytime I implement a method that takes an IControl parameter, I have to cast it to Control to invoke Control spefic methods like setTooltip() or Node specific methods like isVisible() or getBoundsInLocal().
A Solution
From the many constructive discussions I have had with Richard Bair, Jonathan Giles and other JavaFX API developers, it is crystal clear to me, that backwards compatibility is valued immensely – and with good reason. The upside is that new versions of JavaFX does not break any code – yours, or mine, or the UI of a critical application used at a hospital or a nuclear power plant. That is a good thing. However, the downside is, it is now very hard to fix the missing interfaces problem.In an ideal world, I would like a dozen JavaFX classes to be rewritten as interfaces. The existing Control class would be renamed to ControlBase, allowing it to implement an interface called Control. Obviously this would immediately break the code of each and every JavaFX application on the planet, so that idea does not go on my Christmas wish list this year. However, we are not prevented from introducing an interface of a different name. So while we cannot get
NodeBase implements Node
ParentBase implements Parent
ShapeBase implements Shape
ControlBase implements Controlwe can get
Node implements SceneNode
Parent implements SceneNodeParent
Shape implements SceneShape
Control implements SceneControlwhere each of the SceneX interfaces simply contain all of the public methods of their class counterpart.
Conclusion
Admittedly, this does not follow any de facto Java naming standard, but it does work – without breaking any existing code, these interfaces solve the design problem. I can now rewrite my IControl interface asinterface IControl extends Control {…}thereby requiring all IControl's to be real Controls. If I have very finicky requirements for an input parameter, I can even specify new interfaces which combine requirements, like
interface Demanding extends Control, LooksLikeADream, SmellsLikeRoseswhich means that a method requiring a Demanding parameter will get exactly what it needs.
{…}
I warmly welcome any constructive feedback on this suggestion, either here or on the openjfx-dev mailing list, before I post a feature request to the JavaFX team.

Very interesting, I noticed that there are very few interfaces in JavaFX but I don't think it is a problem. It would be almost impossible to re-implement Node with the correct behaviour without it having bugs.
ReplyDeleteExtending controls also has some issues. For example, if you create an InternationalizableTextField and I create an AutoCompleteTextField, and then want to share behaviours we are at an impasse. Most requirements can be met by using listeners, although it certainly wouldn't be fun adding a listener to every TextField.
This comment has been removed by the author.
ReplyDeleteYou can do something like: https://gist.github.com/4065802
ReplyDeleteIt is not very convenient, but I think does what you want?
You are right, Ivan. But the problem with that approach is that it forces me to scatter the
ReplyDelete<T extends Control & IControl>
all over the API. It works, but as you say... not pretty.
This comment has been removed by the author.
ReplyDeleteInterfaces for controls would defiantly be a nice feature. I especially would like to supply my own implementation of ListView.
ReplyDeleteBut as for the core classes like Node I think that every alternative implementation would have to be tied to the non-public APIs of the rendering engine. Correct me if I'm wrong but for the Node to be totally decoupled I think you would really have to implement a more complex interface with methods for the rendering engine.
On the other hand I'm wondering if anyone is considering to take JavaFX rendering to the Web in the long future. Like for example Eclipse RAP does with SWT or maybe with an approach like GWT based on j2js compilation. Interfaces instead of classes could help with this.