Every single time the subject of possibly adding operator overloading into Java comes up, the single justification that is used for keeping it out always falls back to the designers' claim that C++ has proven by example that operator overloading makes code almost impossible to maintain, and so the designers are heavily reluctant to introduce it into Java. Quite frankly, I can genuinely understand this concern.
However, I would point out to people who would abide by this notion that good C++ programmers do not create unmaintainable code simply by using operator overloading because they realize that operator overloading is not merely a shorthand convenience, but they utilize it as a means of creating highly readable code, adhering to the conventions that are typically associated with the operators they overload. To argue that Java ought to be utilizable by anyone, regardless of their skill, to create programs that will rarely be difficult to maintain, at least insomuch as the language is able to ensure it, is not dissimilar from the mindset that gave birth to programming language abominations such as COBOL. To paraphrase Obiwan Kenobi, "that path leads to the dark side".
Okay... let's get serious again. Admittedly, the number of general cases for which operator overloading would make sense is almost certainly quite small... probably less than a dozen in total... mostly within the domain of mathematical or scientific computing. A person who was seeking a compromise with an advocate of operator overloading might suggest that since the number of types is so tiny, perhaps if Java provided these types as standard parts of their library, and provided pre-defined overloaded operators for those particular types, if that would be sufficient.
Unfortunately, the answer would most probably be no. Because although it is true that operator overloading only makes sense on a few fundamental types, the exact implementation of those types is all too commonly something that the implementor must create by hand so that it is tuned for the needs of the software being developed. One person might need a 2-dimensional vector, while another might need a 3-dimensional vector, and yet another might need a 4-dimensional vector. In each case, it often makes sense for the implementor to write their own rather than use a general class that supports varied numbers of elements. And this is just one example of what I am talking about. This sort of thing happens with overwhelming frequency with any field, group, or ring that a programmer might want to utilize, even if they don't happen to realize that they are utilizing such such notions. Not that I'm suggesting it is always wrong to use a general class, of course, but it's still often the case that when the program being developed does not require the level of generality supported by a common class, that a custom one will be developed. This phenomenon causes the number of actual cases for which operator overloading would be potentially useful and actually create more legible code to explode exponentially.
Now that said, I can certainly agree with people who would say that it is not much more work to type a.plus(b) than it is to type a+b, but when trying to chain such operations together it gets a lot more dicey.
z1=a*a-b*b+x;
z2=2*a*b+y;
is simply much more legible than the equivalent where method names must be used:
z1=a.times(a).minus(b.times(b)).plus(x);
z2=a.times(b).times(2).plus(y);
To that end, the operators that I would say that operator overloading is probably really needed for are only the standard arithmetic operators: +, -, *, /, and %. Other operators could probably benefit from being overloaded in circumstances where the result is more legible code, but I do not think their need for it is as imperative as it is for the regular infix operators. Further, I would argue that operator precedence should always be the same as it is for the built in integer and floating point types. While I can see some use for deviating from it, I do not believe the cases for which it would most strongly apply are as common as fields, groups, or rings are.
Now that said, the argument that opponents to operator overloading in Java remains... that it makes it too easy to create code that is difficult to maintain or understand... although I would put it to them that this is only the case when the implementor deviates from the conventions of the operator they are overloading, and tries to utilize the language facility as a shortcut rather than a means of creating more legible code. Just as custom shorthand is frequently illegible to anyone other than the person who wrote it, this type of taking shortcuts in programming creates code that is just as obfuscated. I would argue that it is akin to programmers who habitually use single-letter variable names in production code, although I recognize that even then, single letter variable names can be utilized to create code that is still easily understood as long as the conventions for that particular variable name are upheld. For example, using 'x', 'y', and 'z' as spatial coordinates, or i and j as for loop indices. It is also worth pointing out that as easy as it is to create entire programs with one-letter identifiers, the Java language does not forbid that form of writing obfuscated code. Yet, for some reason, operator overloading in Java still remains off limits.
Okay... so maybe I've convinced some people who didn't want operator in Java that there are some particular cases where operator overloading MIGHT be useful, but some nagging doubts remain because of concern that it would be too commonly abused.
One thing I can think of that might help this matter somewhat is to introduce the notion of the compiler being able to detect a couple of things about the function. First, it would have to be able to identify the "constantness" of the parameters passed into the overloaded operator functions. For an instance to be considered as "constant" in this respect, a few rules apply. 1) none of its data members must be modified for the duration of the function. 2) it must not call any functions with that instance as the object or a parameter where the corresponding object is treated as non-constant. 3) the instance must not be assigned to any variable that is not specifically local to the function. 4) if the instance is assigned to any other variables, then those variables must exhibit "constness" for their own scope, even if the actual flow of the function would not actually ever let the instance get modified in any remotely possible use-cases, it would be too difficult to detect accurately otherwise. The other thing the compiler must be able to detect in a function is that it be side-effect free, which means it does not modify any data other than either its parameters or local variables. For functions which return values, a side-effect free function would always return a new instance of an object. Functions with such side effects generally will be deviating from the normal interpretation of an operator when it comes to using them to overload the operator, and to the extent it is detectable, the compiler ought to flag them, albeit with a warning rather than an error, to maximize generality. For a programmer to suppress this warning, an explicit annotation should be required in the source code so that it is obvious to people who read the code. To that end, if the compiler could detect these characteristics (and it is technologically possible, so it could be written to do so, although existing libraries would need to be recompiled for the compiler to be able to detect it, and function within older class files would default to not being utilizable within an overloaded operator function at all until they were recompiled), then to some extent abuse could be minimized by requiring such characteristics from any operator overloading methods which those features would most logically apply. For example, the normal infix mathematical operators ought to exhibit "constantness" both for 'this' and their single parameter. Of course, again the compiler probably should not disallow such things entirely, but should actually issue warnings that are only suppressible with explicit annotation inside the java source file. This requirement gives the flexibility to people who desire operator overloading for some convenience, but at the same time ensures that such usages are explicitly documented and easy to identify when reading the code. Now of course, even then I have little doubt that there are ways around the mechanism I've described, but I think that the above would go some distance toward accomplish the desired goal of people using operator overloading to create legible programs rather than obfuscated ones.
Now above, I had made an almost cursory reference to annotating usages that deviate from the normal operator conventions. To that end, the source-code annotation I would recommend should be at the actual point of use for the overloaded operator, rather than simply at its definition (in fact, I might argue that annotation should probably occur in both places), so that is always clear that some deviation is occurring from the otherwise typical conventions for the operators being utilized.
Now would the above recommendations stop people from using operator overloading as a programming shorthand? Probably not... but I would maintain that such imperfections in the methodology are probably not fatal to the Java language development community. The usefulness for operator overloading in the domain of mathematical and scientific programming remains, and although I have to concede this is not a primary usage for Java, it's by no means an unheard of or even particularly uncommon type of programming. Properly used, operator overloading results in programs that are _more_ legible rather than actually more difficult to maintain, as the designers of Java felt it could result in. Java would, in the end, benefit from such a facility, and I remain resolutely convinced of that.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment