Event object classes in the Swing event hierarchy have something of a design flaw, in that they use subclassing merely for member inheritance, but without regard to reference type polymorphism.
For example, the ComponentListener interface defines four notification callback methods that receive ComponentEvent instances: componentHidden(), componentMoved(), componentResized() and componentShown(). However, these notification methods are designed so that only ComponentEvents will be delivered to them; ComponentEvent subclass instances are not supposed to be delivered by convention. The listener interface basically treats the event object class as if it were final. The ComponentEvent class is a supeclass for multiple different Swing event types: ContainerEvent, FocusEvent, InputEvent (which has subclasses KeyEvent and MouseEvent, which have further subclasses), PaintEvent and WindowEvent. All these subclass types of course inherit all the member type definitions defined in ComponentEvent. But the Swing framework assumes these subclasses will not be used where a ComponentEvent is expected.
Figure 1 is a class diagram showing the relationships between some of the AWT/Swing event object classes and listener interfaces.
This is a case of using inhertance to duplicate members, but which ignores type polymorphism. This system introduces runtime type ambiguity. For example, a MouseEvent could be delivered to any of the ComponentListener interface methods -- what would the meaning of this be? Clearly its some kind of mistake, but the problem would not be caught either by the runtime compiler, nor the Java runtime, and could cause strange side-effects in an application program.
With AWT/Swing event classes and instances we can see another aspect of the problem by looking at the getTypeID() method that's a member of all AWTEvent-derived classes. For any particular class, instances are supposed to return one of a few finite values from this method. The ComponentEvent class defines 4 constant int values: COMPONENT_MOVED, COMPONENT_RESIZED, COMPONENT_SHOWN and COMPONENT_HIDDEN. But in fact most ComponentEvent objects use completely different type ID constants. For example a MouseEvent, which is a COmponentEvent (i.e. "IS A" ComponentEvent) will not have one of these 4 type IDs. What happens when a ComponentListener receives a ComponentEvent whose type ID value is not one of the 4 proscribed ID values? Maybe nothing, or maybe a wierd side-effect.
In general this is going to be a problem when using a similar mechanism to model category/instance relationships (as opposed to modeling sub- and super-set and set membership relationships). That is, when using classes to model categories and object instances to model members of those categories. Category membership is exclusive: a member of a parent category is not a member of an sub-category. Object instances and their respective classes do not share this relationship, and so make a poor model.
We can design a framework for modeling categories and membership that would avoid the ambiguity problems, but otherwise would have the same features of the AWT/Swing event hierarchy design. This class hierarchy framework is characterized by a "backbone" of abstract base classes and concrete, final "leaf" classes representing categories. The listener interfaces would reference only the "leaf" classes, which would effectively remove the type ambiguity problems.
In this system, the AWTEvent base class would be directly extended by a set of abstract classes intended for extension. These would be the "backbone" classes in the class hierarchy. Each "backbone" class could be extended by concrete "leaf" classes, which would be referenced by the listener interfaces.
Figure 2 is a translation of the classes and interfaces in Figure 1, applying the "backbone" of abstract classes idea to better model event categories.
4:25:11 PM
|