Operator overloading is a topic that never fails to generate very passionate responses, and this monster thread on Artima is no exception.
First of all, I’d like to address a common complaint heard from people who dislike operator overloading, and which usually goes along the lines of:
In Java, when I see a + b, I know exactly what is going on. If Java supported operator overloading, I would have no idea what + means.
I have very little sympathy for this argument because this example is hardly different from a method call such as o.init(). When you read such code in Java, you don’t necessarily assume that it’s the method init on o‘s class that is being invoked. You know that you will have to look at how this object is created in order to determine its effective type before you can find out which method init is being invoked.
Operator overloading is no different, and if you knew that the language that you are reading supports it, you are just going to extend this mental path to operations that involve overridable operators.
Very often, I find that operator overloading is being demonized because its uses in other languages has led to excesses. I have found that many people who dislike operator overloading can trace it back to some personal experiences where they once had to deal with code that was clearly abusing this feature. This is unfortunate, but I still think that Java is doing fine without it and that overall, it only leads to clearer code in very few and uncommon cases.
Here are a couple of objections I have with operator overloading:
- The number of operators that you can overload is very small and each of them is attached to very specific semantics that makes little sense outside the realm of scalars and of a few other specialized mathematical concepts (e.g. matrices).
- In exchange for infix notation, operator overloading severely restricts my freedom to name my methods in a meaningful way.
The most common operators that people want to overload are + and -, and they are also the operators that probably make the most sense outside simple arithmetics. I can see these two operators make sense for types that represent a collection of elements (java.util.Collection obviously, but also strings or streams). However, even in this ideal case in favor of operator overloading, the metaphor quickly breaks down when you realize that you need to supplement these two methods with ones that have slightly different meanings, such as addAll(). Would you rather have an interface that contains add() and addAll() or +() and addAll()?
Of course, there are more operators that can be overloaded, such as * or /, but I find that even strong advocates of operator overloading quickly run short of arguments when trying to justify why one would even want to overload them outside of specific algebras.
I am a big fan of clear names, and I dislike the name + for the simple reason that it doesn’t carry enough meaning to explain to me what is going on. This operator is very clearly defined when it is used in a mathmatical sense but it becomes very vague when you start to extend it to concepts that supports the idea of addition in a more vague sense. For example, adding to a List is very different from adding to a Map since the latter can produce an object that’s equal to the one you started with, even though the object you added is not zero. And what is zero in the sense of a collection, anyway? Doesn’t it make much more sense to use add() for a List and put() for a Map?
Overall, I find that code that makes heavy use of operator overloading is harder to read and harder to maintain because it is severely restricted syntactically (forced to use specific names taken from a very small pool) and semantically (since you are using symbols that have very precise mathematical definitions, it is very unlikely that your usage will match the mental model that people who read your code have of their meaning).
I would love to see some good examples of operator overloading, if you have any to share…