|
Re: What Are Your Java Pain Points, Really?
|
Posted: Feb 11, 2007 9:11 AM
|
|
I'm glad you asked this question -- it's an excellent one.
I think some of the previous posts have been spot on about the general ugliness and inconsistency of the Java APIs, as well as in pointing out some clumsy implementations of essential classes for dealing with dates, numbers, and so forth. As bad as this stuff is though, over time, even these have been supplanted for me by my current top three Java pain points, which are, without further ado (drum roll please):
1. Java reflection is godawful. Method overloading -- a bit of syntactic sugar with little practical value -- greatly complicates the issue, but the implementation is horrible anyway. Java really ought to have a relatively simple and consistent set of APIs to provide this functionality.
2. No real class methods. In Java, classes aren't proper objects, and therefore don't have methods. They have constructors (which are not methods) and they can have static 'methods,' which are nothing more than functions, and are therefore not extensible, i.e. you can't override them in a subclass. Constructors are lame. They don't have return values, and they require calls to super to occur on the first line of code, both of which tremendously limit their flexibility. If constructors were methods, then I could, for example, return an instance of a subclass -- something I frequently would like to do. Constructors also have weird inheritance rules that unnecessarily complicate their usage in subclasses.
The lack of true class methods in Java has led many developers to adopt the Factory (anti-)pattern. Since that approach is terribly flawed IMO, in time, hacks to the compiler (or the runtime) began to surface to support AOP. That leads me me to wonder -- if things like this are so fundamentally broken in Java, why do the spec leads focus all their energy on new language features like generics and closures?
3. Lack of dynamism. Because method invocations are statically bound in Java, we are forced to provide a cast in order to call a method on an object typed to a superclass. Note that I'm not arguing against facilities that allow casting; that's obviously a good thing. However, there are cases where it would be extremely useful to not be forced to cast to a more specific subtype, though of course that would necessitate adding language-level support for dynamic binding.
For example, suppose you needed to implement generic copy and paste behavior. The objects to which these behaviors might apply could be files, bitmaps, sounds, graphics, etc., so you might be dealing with objects from different class hierarchies. It would be convenient to have these objects provide their own implementations of cut, copy and paste, since the implementations would vary according to type. User interface code could simply call these methods, as needed, without needing to know which specific subtype it was currenlty dealing with.
Java interfaces would seem to provide a way to deal with this situation, but unfortunately that approach has often proven to be too inflexible. For one thing, all the relevant code has to be compiled against the same interface file, which in many cases is simply not possible. This approach can also lead to backwards-compatibility nightmares when for new versions of an interface. Some previous posters in this thread have proposed mixins as a solution, though dynamic binding (especially with the addition of improvements to Java reflection) seems to me to be a simpler approach, as shown in the following example (with a nod to Objective C):
private Object selectedObject;
public void cutSelectedObject() { if (selectedObject.respondsTo("cut()") { selectedObject.cut(); } }
The preceding code snippet assumes the addition of a respondsTo method to the Object class, and that the Java compiler and runtime have been modified to support dynamic binding. This would allow the interface that defines the cut, copy and paste methods to be provided as a specification, rather than as a compile-time artifact. Calling code could then proactively check to see if an object responds to a given message before attempting to send it, and in this way avoid runtime exceptions.
|
|