Operator Overloading Considered Harmful

The gates seem to be open for serious revisions to the Java language in Java 7. Thus it’s worth reviewing why some things are just flat out bad ideas for any language, because too many developers either never knew this or have forgotten it. First up: operator overloading:

It’s not a coincidence that almost every C++ text written in the last ten years recommends extreme caution when overloading operators. For example, here’s Bruce Eckel in Chapter 10 of Thinking in C++:

It’s very tempting to become overenthusiastic with operator overloading. It’s a fun toy, at first. But remember it’s only syntactic sugar, another way of calling a function. Looking at it this way, you have no reason to overload an operator except that it will make the code involving your class easier to write and especially read. (Remember, code is read mcuh more than it is written.) If this isn’t the case, don’t bother.

If anything, Eckel is too kind to operator overloading. What’s really wrong with operator overloading? Let me count the problems:

Illegible Code

The most serious problem is that operator overloading leads to illegible code. You can no longer tell what any given chunk of code is doing. Sometimes it’s obvious, as when someone is adding two complex numbers. Most of the time it’s not. For instance supose someone is dividing two matrices. What does that mean when one matrix isn’t invertible? Worse yet, suppose someone adds two database records? or subtracts two addresses? Outside a very limited mathematical realm, operators don’t have any well understood meanings. It’s much better to have a full method name such as subtotalOrder() or distanceTo().

Few use cases

There are some good uses cases for operator overloading. Complex arithmetic is one. BigInteger and BigDecimal are two more. These are all instances of mathematical rings or at least groups, where the operations of addition, multiplication, division, and subtraction are well-defined. (Groups may only define addition and subtraction.)

However, there just aren’t that many groups, rings, and fields out there which are likely to come up in most programmers’ every day practice. By my count they’re only maybe ten or so more that really make sense.

If something isn’t a group or ring, then the various symbols just don’t fit it. A full-blown method name will be more descriptive, more accurate, and less likely to confuse readers.

Precedence

Do you remember all the precedence rules of high school algebra? Do you remember all the precedence rules of your language of choice? (Personally, I answer yes to the first and no to the second.) You need to know sets of rules both inside and out to have any hope of getting operator overloading correct. You can’t just bang out a + method and expect it to work.

That’s not all either. To properly overload an operator you need to understand transitivity, commutativity, and distributivity; when they do and don’t apply; how they relate to the various operators; and how they match method invocations.

Maybe you’re a math hot shot and can keep all this straight, but do you trust the guy down the hall from you to be equally competent? Remember, it’s his code you’re going to have to debug.

Keep in mind, many programmers can’t even reliably write an equals() method that satisfies basic commutativity requirement that a.equals(b) ==> b.equals(a) . Do you really want to trust these same doofuses to follow the much more stringent requirements for +, -, *, and /?

P.S.

Many people object that Java already has operator overloading. For instance, you can use the plus sign both to add ints and to concatenate strings. Anyone making this argument does not understand what’s at issue, and their opinions can be discounted.

Using the same symbol for two different built-in operations is completely different from operator overloading. For instance, almost every language supports this including languages like Basic and C. Adding two ints is not the same operation as adding two doubles, or as adding an int to a double. However, all three different operations use the plus sign in most languages.

This is not a problem because the plus sign is still unambiguous. It means the same thing in everyone’s code. There’s no confusion. This is completely different from allowing my code to mean one thing when it adds two database records and your code to means something completely different.

Operator overloading just doesn’t make sense. While there are legitimate use cases for it, there aren’t enough of them. While it makes some code cleaner, it makes much more code dirtier. While it can be used competently by the most cautious programmers, it is far more likely to be used incorrectly by the rest of us. In C++ operator overloading is a major contributor to illegible code, unmaintainable code where bugs hide. Operator overloading: just say no.

29 Responses to “Operator Overloading Considered Harmful”

  1. dbt Says:

    Modern template programming often depends on operator overloading to do simple operations to arbitrary types. Being able to treat iterators and pointers as peers for example, you often want to overload ++ and –, and maybe += and +(int). Arbitrary objects often want a double-less-than, which is just C++ for .toString(). It’s pretty useful, as you say, in moderation. I don’t think that adding it to java at this late date does anything more than confuse people.

  2. dbt Says:

    And that’s without getting into the awesomeness of being able to overload []. And arbitrary type containers that overload ->.

    Always something that should be done in such a way that makes using the code easier and more obvious, and not for the sake of “look, I know how to use operator…

  3. Marcus Says:

    Thankfully in the second half of this decade the notion of Java having its lunch stolen from it by all the little languages that could has shocked enough people into not listening to advice like this as much any more.

    Frankly I find it amazing that still, today, people hold onto the sort of doctrine stated above, completely ignoring the wealthy of successful uses of powerful features like operator overloading. Regardless, Java has made great strides, and isn’t as held back any more by such narrow thinking. Just in time too.

  4. John Cowan Says:

    I agree completely and I disagree profoundly.

    Misused, operator overloading is a horror. It makes you think you understand when in fact you understand nothing. It leads the debugger’s mind up the garden path and leaves it up S**t Creek without a paddle.

    On the other hand: when you run into one of those programmers working in fields or integral domains or rings or groups, and they think you might possibly be able to give them overloaded operators, they will shake you until your teeth fall out. The alternative is profoundly unacceptable, unless you are programming in Lisp and put all your operators in prefix anyway.

    In other words, when you don’t need operator overloading, you mustn’t use it. When you need it, you really do need it. What’s a poor language designer to do?

  5. aha42 Says:

    Its needed for true generic programming, there is a reason java is dragged by its ass into c++…

    Sure most lowlife programmers should stay away (but will not and give operator overloading a bad name, but what can you do…) but is needed for skilled library designers.

    Bring it on!

  6. Nomadman Says:

    The interesting thing about the commentary to this post is that, it seems to me, the main argument against Java these days is not its lack of operator overloading. On the other hand, when people complain about Java, they complain about how bloated and complicated it has become and how many over-engineered APIs you need to master to do basic things. It does not look to me like operator overloading is going to help Java stay away from even more complexity. If operator overloading was such a crucial feature missing in Java, why did Java become the most popular language in the world? Why is C++ arguably the most easier language to write bug-ridden software on according to many? Yeah, yeah, I’m sorry. I did not mean that… Well if you disagree so strongly why do you care to code in Java anyway… why not just program in C++ then which has all those great features?

    Let’s improve Java even more yet. Let’s get rid of Strong typing, add Tupples, and, and… wait, that is Python. Damn it’s hard to innovate these days.

  7. Werner Says:

    I would like to be able to use operators. Writing a + b instead of a.plus(b) seems fine to me. Defining other operators would be great. I don’t understand your objections, esp. when one of your model languages, Eiffel, shows how easily it could be done.

    Conversion problems: Maybe it is a mistake that is so easy to convert int into double and vice versa. But as long as they stay as primitive types rather than as first class citizens, there is little one can do. I would prefer explicit int.toDouble(), double.toInt() methods.

    BTW, operator overloading is a different problem. Overloading only comes into play if a super-class has already defined an operator. Here we want the ability to define an operator in the first place.

  8. Slava Pestov Says:

    How is an inappropriate overload of +, or using operators for things which are not mathematical objects such as database records is no different from some programmer defining a method named gufdgh(). The potential for writing unreadable code exists in any language. The Eckel quote contributes nothing to the validity of your claim, it is simply appeal to authority. It seems you strongly dislike C++. I do too. Have you used any other language with operator overloading? I guess it really is true that Java is aimed at the lower 90% while many other languages focus on the top 10%. “Enterprise programming - just say no.”

  9. martin Says:

    90% of c++ is based on following idea:
    “use templates and operator overloading to imitate behavior of primitive types”.
    c ++ comitee spent 10+ years coping with consequences of this idea.
    Having this done (afaik), it is time for compiler developers to reflect specification.
    I strongly believe, different compilers implement the specification in different levels.
    This effectivelly preserves change compiler (or platform) for Your project.

    I kindly please converted c++ programmers to not pollute java with this mistake.
    If they insist on operator overloading and better templates, they should better
    choose another language, not java. …. please.

    I really do not see a benefit to write a*b instead a.multiply(b) when a and b are matrix.
    This would involve a lot of effort from standard comitee, compiler implementors,
    ide implementors and other tool implementors.

    And why? …. to use it in 0.00000001% of programs?

    bye, martin

  10. Ravi Venkataraman Says:

    Elliotte said, ” Adding two ints is not the same operation as adding two doubles, or as adding an int to a double.”

    Actually adding two numbers, real or complex, is essentially the same operation. It is an artifact of programming languages that they make distinction between “int” and “large”, and ‘float” and “double”. This seems to be a common fetaure of the C family of languages (C, C++, Java, C#, etc.). In java, this leads to abominations such as BigInteger and BigDecimal. ( The same holds for subtraction, multiplication and division, of course.)

    As for adding two database records, any programmer who suggested such an idea would probably be reassigned to the important task of keeping the coffee machine working and encuring that other developers are supplied regularly with their preferred caffeinated drink. Such cases are not even worth talking about. There is truly no idiot-proof language.

    Ravi

  11. Firefight Says:

    I remember from my C++ days just how bad things got with operator overloading. I would like using [] for ArrayList item retrieval, and find it verbose to use BigInteger.add(), but given the choice between verbosity and the C++ debacle, I’ll choose verbosity any day.

    There’s no way this will go in jdk7, maybe when Java 3 comes along.

  12. robert Says:

    Let’s see: COBOL programmers add database records as a matter of course, using a cursor and accumulators. Most should be consigned to the coffee machine, but alas, they usually become managers (and grow pointy hair). Read Ch. 7 of Date; he discusses Union (relational Add) and Minus (not implemented, so far as I know). Pascal is right: most coders really don’t know data all that well. Present company excepted, of course. A sql implemented move corresponding with add would make them very happy.

  13. Marcus Says:

    Why on earth does everyone immediately revert to C++ discussions whenever operator overloading comes up? C++ is not, I repeat, not Java’s primary competition for projects in 2007. Discussions of its use or misuse in C++ are worthless to any debate. They simply can’t apply effectively. The problems of an unmanaged language are manifold, and operator overloading is the least of the concerns. C++ has has its time and place, in fact, many of them, but not in discussions of Java language features any more.

    Instead its time to reverse the trend and learn some lessons from other languages. C# got a jump by looking to Java’s successes and missteps. They learned what not to do with exceptions, and went ahead and incorporated operator overloading. Are they suffering from it? And both functional and dynamically type languages are providing valuable lessons to today’s programmers and there is much to be learned.

    It doesn’t mean that Java needs to become dynamically typed. Nor does it need to become functional. However there’s a lot to be learned, and operator overloading is not exactly destroying projects in those languages.

  14. Dave C. Says:

    The thing I _least_ miss about C++ is operator overloading. Overriding equals, implementing IComparable, creating my own, similar interfaces for generic operations: all worth it to avoid the severe pain of debugging code with operator overloads in unexpected places.

  15. Bruce Eckel Says:

    Unfortunately, Gosling did not seem to understand the actual problems with operator overloading in C++, and so he condemned it out of hand. People have been echoing this condemnation ever since.

    The real complexity problems with operator overloading in C++ came from the mixed memory-management approach that it inherited from C. You can create objects on the stack or on the heap. Writing a properly overloaded operator in C++ is very messy because of this, and you must worry about the creation of temporary objects and be very careful about how you return results.

    Ironically, Java solved those problems by using a unified memory management approach with a garbage collector. You create all objects on the heap, the garbage collector cleans them up, and all the complexity problems associated with C++ operator overloading go away. As proof, look at operator overloading in both Python and C#. No surprises or dark corners.

    It sounds like you’re suggesting that by keeping operator overloading out of the language, we’ll prevent illegible code. An inexperienced Java programmer does not need operator overloading in order to write illegible code. Any feature can be misused, and saying that it should be left out because it might be misused is shortsighted.

  16. Frank Wilhoit Says:

    “…Do you really want to trust these same doofuses…?”

    No, I do not. That is why I will always prefer languages in which code written by doofuses will not even compile over languages in which code written by doofuses will compile and [appear to] run. I am not ashamed to admit that I had to work with C for eight years before I reached the point where my code would run correctly first time. I *am* ashamed to admit that I began deploying my code into production (after immense debugging) two years before the end of that window of time. If Java had existed then, I might have become “productive” somewhat sooner but the code would probably have been even worse and there are certain things I might never have learned. Anything that encourages doofuses or their managers to imagine that they know more than they really know, or can do more than they really can, is unconditionally bad.

  17. Marcus Says:

    I think the biggest indication of problems in Java’s original approach toward operator overloading is the “do as we say and not as we do” travesty. The moment the designers decided to allow themselves language privileges not extended to other developers might possibly be the largest single mistake on general principles.

    If a language cannot be written in itself syntactically, without the use of hacks such as source filters, then serious questions need to be asked.

  18. Marcus Says:

    Additionally: “string” + “string” is not unambiguous. “5″ + “10″? Am I making a list? What presumption indicates that plus is the same as concatenation? Claiming “we’re special” is just such an excellent design “smell”, heh.

  19. Slava Pestov Says:

    “Why on earth does everyone immediately revert to C++ discussions whenever operator overloading comes up?”

    Because 90% of Java programmers assume the only other languages which have been invented in human history are C#, C++, and COBOL. Some might know about Ruby. This is almost tautological, because many will switch to better languages as soon as they learn about them and cease to be Java programmers. The ignorant masses compare Java against C++ because they think its the only other language out there.

  20. Slava Pestov Says:

    Regarding string concatenation, everywhere in mathematics you see that + is only ever used to denote commutative functions. Java’s designers completely ignored this convention by using + for strings. Even * would make more sense for string concatenation than +, because * is used to denote the multiplication in a monoid, and the set of strings with concatenation is a monoid.

  21. martin Says:

    let forget personal preferences and think in following terms:
    1. current state - what’s wrong, Your motivation
    2. desired state - what are consequences
    3. is effort worth of the change?

    my answers are:
    1. fine. I am happy with status quo.
    2. heavy language, error prone code
    3. i am not aware of a code, which would benefit from operator overloading.

    my conclusion is - DO NOT DO IT!
    maybe, if someone explain me what motivation started the debatate, i could change opinion.
    now i have suspicion someone wants it because 1. he can now 2. he likes write compilers (a typical programmer :-)

  22. dvholten Says:

    when i lean back and look at the ’sea of code:’ - eclipse, jboss, batik, findbugs, jaspereport, xalan, xerces and not to forget java-runtime - how much and where would they have benefited from operator overloading?? How many bugs less? How much faster progress??
    the added complexity (observed and hidden) is a too high price for marginal benefit.
    The ‘beauty’ of java is it’s ’simplicity’ (ok, its not as simple as it looks…). When we demand back those things the designers left away - why not ask for unsigned ints (there are uses for that) - why not ask for a preprocessor (there are uses for that) - why not ask for complex numbers (there are uses for that)

  23. Stephen Colebourne Says:

    “Keep in mind, many programmers can’t even reliably write an equals() method that satisfies basic commutativity requirement that a.equals(b) ==> b.equals(a) .”

    This requirement can’t be implemented in Java as is.
    : a.equals(null) ==> false
    : null.equals(a) ==> NPE
    An operator for .equals() could fix this issue.

  24. Alx Says:

    “Examples by Maliciousness Intent” are a crummy excuse.

    Why is it that C begat The Obfuscated C Code Contest?

    Don’t use feature X. You *could* abuse it horribly.
    Don’t make electronics equipment available for sale. Someone *could* use it horribly.

    For matrix algebra and group theory, overloaded operators are fantastic, closures too. IF you know mathematics and apply it properly. But it seems so few people are involved in actual *math* by computer. No not arithmetic. MATH.

    Who told you you were to use overloaded operators in GUI design? Do you use a knife as a hammer? Do you use a chopstick as a toothbrush?

    Come on now.

    Operator overloading and in fact any feature is not an automatic problem. They address someone’s needs not necessarily yours. C++ should be banned only because so many programmers refuse to move beyond it and stupid pointer tricks nor learn how Lisp, Forth, Smalltalk, etc work that they assume that everything else either does or should look like C++. Operator overloading works in Smalltalk.

    Stop programming with malicious intent towards your compiler.

  25. Anonymoose Says:

    >>Who told you you were to use overloaded operators in GUI design? Do you use a knife as a >>hammer? Do you use a chopstick as a toothbrush?

    Have you ever had to maintain code that misused language features? You can’t enforce developers to ‘correctly’ use language features. While misuse of language features isn’t a valid reason for not implementing a specific feature I’d argue it’s a smart one.

  26. Ravi Venkataraman Says:

    Re robert’s comments about COBOL programmers using the “+” sign to add database records.

    It seems to me that they are adding records to a collection of records. The + sign usually suggests that there are two “quantities” of similar type and the result of the operation is another quantity of similar type. Also, it is implicit that there is no general way to uniquely decompose the result into the two original quantittes.

    The use of “+” for accumulating database records has a slightly different meaning. Yes, we do add a record to a collection of records, but there the similarity ends. The two “quantities”, record and collection of records are not of the same type; and it is possible to get back the “last” record added if it is an ordered set.

    Hence, the “+” sign should not be used as a shortcut for accumulating records to a collection of records. Although permitted by COBOL conventions, I would definitely not use it.

  27. robert Says:

    I didn’t mean to say that COBOL-ers use “+” as such; so far as I know, it isn’t defined as an overload. Rather that, the process of “+” is executed as a matter of course in that world, and the overload of “+” would have that interpretation were it available: give me one row with the “+” of each numeric column in the rows selected. This is not, however, how a database developer would likely define the meaning of “+” used in such a syntax.

    It does come back to what one means by “+”, thus proving the point that overloading is a loaded subject. If by “+” database rows (they ain’t records) we mean accumulate the total, then yes, COBOL-ers do it all the time as described, and would appreciate such an overload. It would make their lives easier; just ask anyone who’s written an AP module. On the other hand, if “+” database rows means the set of rows meeting the result of multiple statements, that exits with UNION, and MINUS has been implemented as EXCEPT in some database. My error. I don’t have cause to use it.

    As Alx said, “+” is defined for matrix operations. This definition is universal within the profession. If you’re a mathematician, you have a definition. Overloading of operators within the scope of one program, or even a suite of programs is a different kettle of fish.

    Overloading requires knowing (at least) four things: the complete semantics of the operator in its base (original, as far back as we can trace) context, the complete semantics of that context, the exactly equivalent semantics proposed for the operator in the context of code being created, and that a person of average intelligence would find the result of the overload intuitive. That really is a tall order. And likely why only “+” in java; that and the fact that the convention already existed. hmm.

  28. me22 Says:

    In other news, because programmers can’t be trusted to make a function named add actually add its arguments together, we should get rid of functions.

  29. Ingo Says:

    Strange, nobody ever mentioned the built-in ambiguities in methods with names like “add(obj)” or “remove(obj)”. I find it always very confusing that some of those work on their object instance, others don’t change that and return a newly created object with the result. Some - and that’s what really makes it difficult - do both. So often, I have to trace bugs that arise from me expecting a method just to return the result, but on the way changing the instance as well. And vice versa.

    My proposal for (dual) operator overloading is such:
    1.The result is returned.
    2.No object instance is mutated.

    1 is obvious. 2 could be enforced by the compiler - at least I’m sure it could. Just make assignments to this.* invalid. This rids you of the problem (which is, in fact, there in C++ as well). [Granted, there may be ways to do it by side-effects, but hey!, in complex languages like Java you can always work-around something. And if it’s by reflection…]
    Of course, never anybody will be able to keep dumbheads from inventing strange uses for specific operators. But I’ve seen similar things with named methods.

    And there’s oh so many uses for operator overloading. After Java5, it’s the one thing I always said is missing from Java to make it a great programming language. Remember generics? Done mainly with preprocessor in C - baaad. Great implementation in Java. Why not try to do it the same way in Java, getting rid of the problems. Granted, there may be more to it than my two cents above, but that already rids you of one ambiguity…

Leave a Reply