Improved Support for Optional Object Behaviors at Runtime
Alan Snyder
javalists at cbfiddle.com
Fri Mar 27 12:11:36 PDT 2009
Perhaps a few more words of explanation would help clarify the proposal. I
will write them using a FAQ format.
* What is the problem being solved?
The goal of the proposal is to better support objects with optional
behaviors, meaning that the client must perform some sort of test to see
if the the optional behavior is defined and also somehow get access to the
optional behavior using an appropriately typed variable (one whose type is
the class or interface that specifies the optional behavior). Better means
better than the current solution of using a type cast.
* The client code that you recommend (to access optional behavior in an
object) doesn't look that different than what people would write today.
What is the point? Are you saying that an if statement is better than an
exception handler?
Actually, I do think an if statement is better than an exception handler
in this case, but that is incidental. Changing the code that the client
writes is necessary to access the new features of this proposal. Using the
new style, the client invokes a method. Using the old style, the client
uses a primitive operator (type cast). The advantage of invoking a method
is that method invocation is extensible, whereas the behavior of the
primitive operator is fixed (or limited) to what the Java Language
Specification says. When I say that method invocation is extensible, I
mean that the code that gets executed is defined by the developer of the
class of the target object.
* The method definition that you proposed (for class Object) looks just
like the old style code in the client. What is the point?
It is completely intentional that the code looks the same, because that is
how backward compatibility is supported. What this means is that if a
client program is changed to invoke the getExtension method instead of
using a type cast, the program will behave exactly as it did before, when
it is using old objects (that is, objects whose implementations have not
been changed to take advantage of this proposal).
However, the proposed method definition is just a default method
definition. A class can be written that overrides the getExtension method,
and it can do things that a type cast cannot do.
* What can a custom implementation of the getExtension method do that a
type cast cannot?
Good question. The main thing that a method can do that a type cast cannot
is return a reference to a different object than the original target
object. If you haven't thought about this sort of thing before, this idea
might sound strange, but it is really not. The new object would almost
certainly share data with the original object.
Here's an example from current Java: Any collection object can return an
iterator that provides the elements of the collection one at a time, and
may even allow elements to be removed from the collection. The iterator is
a different object than the collection, but they share access to the same
underlying data. Such sharing is not only more efficient than making a
copy of the data for the iterator, it is essential to allow the iterator
to remove elements from the collection.
Because the method can return a different object, it can support class
parameters that are unrelated to the class of the target object. As a
simple, perhaps fanciful example:
File f = ...;
List l = (List) f;
This cast will always fail, because File does not implement the List
interface (and probably never will).
However, consider this call:
File f = ...;
List l = f.getExtension(List.class);
This call might succeed. It won't succeed on an ordinary File, but I could
write a subclass of File for which it would return a List (say, of the
files in a directory, if the File represented a directory).
* You say this proposal better supports delegation. What is delegation and
why is it important?
If you have never encountered delegation before, you probably need to do
some reading before you will fully appreciate it. In a nutshell,
delegation is an alternative to subclassing as a way of customizing object
behavior. You write a new object that supports the same methods as the old
object, and implement the methods by forwarding them to the old object,
but the new class may add new methods, or do other things in the old
methods, or alter method parameters or results. This concept is also
called wrapping. People who know a lot about delegation know that Java
does not support delegation in its full glory, but even a limited form can
be useful.
The problem with type cast is that it can be very hard to make a wrapper
object that simulates the behavior of the original object when a client
uses a type cast on the wrapper object. The reason it is hard is that no
wrapper class code gets executed when the type cast is done. Instead, the
wrapper class must be constructed to simulate the relevant aspects of the
class hierarchy of the original object.
* OK, so the getExtension method is useful, but why not just define it in
a new Interface instead of adding it to class Object?
This can certainly be done, and it has. I know of four public APIs that
have included a method similar to getExtension in their interfaces, and
there are probably many more. The unwrap method in java.sql.Wrapper has
already been mentioned as one example.
However, defining getExtension in a new interface means that it can be
used only on new APIs that contain this method or old APIs that have been
modified to contain this method.
By adding this method to class Object, any class developer can now or in
the future add optional behaviors to their class without changing the
class hierarchy or the API.
This ability is available to Java platform class developers as well as to
application class developers. I have not looked, but it would not surprise
me that some JSR expert group is considering adding some new behavior to
an existing Java platform class. If they used the getExtension method to
provide access to that new behavior, then they would not have to make any
change to the existing API, only to the documentation. Because the API is
unchanged, any application created subclasses would continue to work and
to compile. They would simply return null when asked for the new
extension.
In effect, this one small API change now can remove the need to make many
future API changes.
* Extra credit: If the basic problem is that type cast is not extensible,
why not make it extensible?
Excellent question. Feel free to submit a proposal!
More information about the coin-dev
mailing list