views:

21263

answers:

21

Coming from C++ to Java, the obvious unanswered question is why not operator overload.

On the web some go about: "it's clearly obfuscated and complicate maintenance" but no one really elaborates that further (I completely disagree, actually).

Other people pointed out that some objects do have an overload (like String operator +) but that is not extended to other objects nor is extensible to the programmer's decision. I've heard that they're considering extending the favour to BigInt and similar, but why not open that for our decisions?

How exactly if complicates maintenance and where on earth does this obfuscate code?

Isn't :

Complex a, b, c; a = b + c;

much simpler than:

Complex a, b, c; a.equals( b.add(c) );

??? Or is it just habit?

+3  A: 

Well you can really shoot yourself in the foot with operator overloading. It's like with pointers people make stupid mistakes with them and so it was decided to take the scissors away.

At least I think that's the reason. I'm on your side anyway. :)

Corporal Touchy
+5  A: 

I think this may have been a conscious design choice to force developers to create functions whose names clearly communicate their intentions. In C++ developers would overload operators with functionality that would often have no relation to the commonly accepted nature of the given operator, making it nearly impossible to determine what a piece of code does without looking at the definition of the operator.

+2  A: 

The Java designers decided that operator overloading was more trouble than it was worth. Simple as that.

In a language where every object variable is actually a reference, operator overloading gets the additional hazard of being quite illogical - to a C++ programmer at least. Compare the situation with C#'s == operator overloading and Object.Equals and Object.ReferenceEquals (or whatever it's called).

Sebastian Redl
A: 

Overloaded operators can expose functionality that is not always clear so the code stops being self documenting. If you have to define methods, then descriptive names can document code effectively.

scubabbl
Not necessarily, I can create a method add(foo) that increments an internal value (instead of the logical side effect) and that would be as bad as operator overloading...
+1  A: 

Assuming Java as the implementation language then a, b, and c would all be references to type Complex with initial values of null. Also assuming that Complex is immutable as the mentioned BigInteger and similar immutable BigDecimal, I'd I think you mean the following, as you're assigning the reference to the Complex returned from adding b and c, and not comparing this reference to a.

Isn't :

Complex a, b, c; a = b + c;

much simpler than:

Complex a, b, c; a = b.add(c);
David Schlosnagle
Am I? ;) Equals can both mean assignment or comparison, but = is always assignment and == is always comparison. Names can introduce big sources of errors themselves.
A: 

When reading code and fixing bugs, all it does is obfuscate code. It is a huge pain to have to double check if a variable is of the basic types (i.e. integer, float, etc.) or an object each time you see an operator. Especially if the object is not defined in the code block your looking at but in some other class definition file.

Herge
"all it does" is "all I've heard" but it can ease the reading as well. Ruby went the other way around and made objects of primitive types and no one complains that's a bad thing.
A: 

Complex a, b, c; a.equals( b.add(c) );

This doesn't make much sense. Looking at it I'd say you're adding c to b, and checking whether a equals to the new b.

How about: Complex a = Complex.add(b,c); ? Maybe it's a matter of acquired taste, but I think this too is quite readable.

In the end of the day, I guess that the only conclusion I could draw about all this was that it was just a matter of taste anyway... ;)
For anything more than trivial it gets ugly. a.set((a*b + b*c)/5) or a = a.multiply(b).add(b.multiply(c)).divide(5)
B T
+1  A: 

Groovy has operator overloading, and runs in the JVM. If you don't mind the performance hit (which gets smaller everyday). It's automatic based on method names. e.g., '+' calls the 'plus(argument)' method.

noah
+11  A: 

@David

Assuming that the SCdF wanted to overwrite the previous value of the object refered to by 'a', then a member function would have to be invoked.

    Complex a, b, c;
    ..
    a = b.add(c)

In C++, this expression tells the compiler to create 3 objects on the stack, perform addition, and copy the resultant value from the temporary object into the existing object 'a'.

However, in java, operator= doesn't perform value copy for reference types, and users can only create new reference types, not value types. So for a user defined type named 'Complex', assignment means to copy a reference to an existing value.

consider instead:

b.set(1, 0); // initialize to real number '1'
a = b; 
b.set(2, 0);
assert( !a.Equals(b) );

In C++, this copies the value, so the comparison will result not-equal. In Java, operator= performs reference copy, so 'a' and 'b' are now refering to the same value. As a result, the comparison will produce 'equal', since the object will compare equal to itself.

The difference between copies and references only adds to the confusion of operator overloading. As Sebastian mentioned, Java and C# both have to deal with value and reference equality separately -- operator+ would likely deal with values and objects, but operator= is already implemented to deal with references.

In C++, you should only be dealing with one kind of comparison at a time, so it can be less confusing. For example, on Complex, operator= and operator== are both working on values -- copying values and comparing values respectively.

Aaron
Just FYI, I didn't ask this question, I only edited the tags. Click the time (it currenty say 'an hour ago') above my name for more info-- It looks like Rengolin (http://stackoverflow.com/users/14067/rengolin) asked the question.
SCdF
It's pretty simple really... Just do like Python and have no overloaded assignment.
Longpoke
This answer doesn't answer the question at all. You're simply harping on java's use of the equals sign. If b+C returned a new Complex, then a = b+c would be perfectly valid, and yes much simpler to read. Even if you wanted to modify a in place, a.set(b+c) is a ton simpler to read - especially when the arithmetic is any more than trivial:a.set((a*b + b*c)/5) or a = a.multiply(b).add(b.multiply(c)).divide(5). Your choice..
B T
Or I guess.. not your choice, as the case may be
B T
In C++, Expression Templates solve the problem of the extra copy. Pretty much all major arithmetic libraries use this technique for this very reason. Also, this doesn't address the question, since a = b + c is just syntactic sugar for a.foo(b.bar(c)), which is really the initial observation in the question.
Kaz Dragon
+8  A: 

James Gosling likened designing Java to the following:

"There's this principle about moving, when you move from one apartment to another apartment. An interesting experiment is to pack up your apartment and put everything in boxes, then move into the next apartment and not unpack anything until you need it. So you're making your first meal, and you're pulling something out of a box. Then after a month or so you've used that to pretty much figure out what things in your life you actually need, and then you take the rest of the stuff -- forget how much you like it or how cool it is -- and you just throw it away. It's amazing how that simplifies your life, and you can use that principle in all kinds of design issues: not do things just because they're cool or just because they're interesting."

You can read the context of the quote here

Basically operator overloading is great for a class that models some kind of point, currency or complex number. But after that you start running out of examples fast.

Another factor was the abuse of the feature in C++ by developers overloading operators like '&&', '||', the cast operators and of course 'new'. The complexity resulting from combining this with pass by value and exceptions is well covered in the Exceptional C++ book.

Garth Gilmour
A: 

Sometimes it would be nice to have operator overloading, friend classes and multiple inheritance.

However I still think it was a good decision. If Java would have had operator overloading then we could never be sure of operator meanings without looking through source code. At present that's not necessary. And I think your example of using methods instead of operator overloading is also quite readable. If you want to make things more clear you could always add a comment above hairy statements.

// a = b + c
Complex a, b, c; a = b.add(c);
DeletedAccount
Of course, as mentioned elsewhere you can never be sure of the meaning of the add function either.
Eclipse
True, I still find it comforting to know that at least my operators are hard coded. Of course, having the features and using them sensibly would only do us good. The problem is that it's hard to know if someone has used them sensibly. And that you agree on the definition of sensibly. :-)
DeletedAccount
A: 

Why is it that you think the version with overloading is more reasonable?

How often do programmers need to implement new fundamental integer data types?

Can you think of an example other than complex numbers where overloading actually improves readability?

Jonathan
Matrices. Math Vectors. "trilean" type (true/false/unknown). Physics equations (i.e. dividing distance with time results in speed). Array access through operator []. Functor feature through operator(). Type comparisons through operator < and == (for example, GUID)...
paercebal
Also see theThunk's answer above re: dimensional analysis. Would http://marsprogram.jpl.nasa.gov/msp98/news/mco991110.html have happened if they'd used a language that allowed Boost::Units or equivalent ?!
tragomaskhalos
+2  A: 

Check out Boost.Units: link text

It provides zero-overhead Dimensional analysis through operator overloading. How much clearer can this get?

quantity<force>     F = 2.0*newton;
quantity<length>    dx = 2.0*meter;
quantity<energy>    E = F * dx;
std::cout << "Energy = " << E << endl;

would actually output "Energy = 4 J" which is correct.

A: 

Related question: What is your favourite overloading of the . operator?

Asgeir S. Nilsen
the "dot" operator is not overloadable in C++. See http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B
paercebal
A: 

Writing XML is easy with operator overload:

return ( entry( ac = "P12345", name = "Foo", value=1 )
      << sequence ( md5 = "da4234ddfe712" ) );

And my favourite overload of the . is:

T &operator.() { return t; }
A: 

The problem is not that there are no good uses for operator overloading; the problem is that there are lots of bad uses for operator overloading.

Particularly when you get to overloading . and new.

Edit:

Ok, so you can't overload . in C++. But I stand by the general point that overloading has rather few accurate mapping of operators for objects: mostly interesting number (complex etc), points, matrices. I think even the stream overrides are abusing the mechanism.

I think overloading the new operator can be used to do very clever things, but I suspect that I would really hate debugging anything that used it.

The problem with looking at operator overloads in the standard library is that they'll be familiar to all developers (in that language). That makes it hard to understand where the maintenance complexity comes from.

Douglas Leeder
Err... the "dot" is not overloadable in C++. As for new, it have its uses for memory management and/or debug. Who's abusing "new" anyway? It's not like your average programmer will overload new just for the fun of it: new is not "fun", unlike, say, ++ or <<.
paercebal
There are lots of good uses for operator overloading. Any user-defined numeric class has a really good use case for this.
B T
@B T I mentioned numbers, and other good cases, and also said that the problem wasn't the lack of good use cases. It's the bad cases that cause the problems, and make the feature cutable. Basically your comment is precisely repeating what is already in my answer.
Douglas Leeder
+37  A: 

There are a lot of posts complaining about operator overloading.

I felt I had to clarify the "operator overloading" concepts, offering an alternative viewpoint on this concept.

Code obfuscating?

This argument is a fallacy.

It is as easy to obfuscate code in C or Java through functions/methods than it is in C++ through operator overloads:

// C++
T operator + (const T & a, const T & b) // add ?
{
   T c ;
   c.value = a.value - b.value ; // substract !!!
   return c ;
}

// Java
static T add (T a, T b) // add ?
{
   T c ;
   c.value = a.value - b.value ; // substract !!!
   return c ;
}

/* C */
T add (T a, T b) /* add ? */
{
   T c ;
   c.value = a.value - b.value ; /* substract !!! */
   return c ;
}

For another example, let's see the Cloneable interface in Java: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Cloneable.html

You are supposed to clone the object implementing this interface. But you could lie. And create a different object. In fact, this interface is so weak you could return another type of object altogether, just for the fun of it:

class MySincereHandShake implements Cloneable
{
    // etc.

    public Object clone()
    {
       return new MyVengefulKickInYourHead() ;
    }
}

Should the Cloneable interface be banned on the same grounds C++ operator overloading should be?

We could overload the toString method of a MyComplexNumber class to have it return the stringified hour of the day. Should the toString overloading be banned, too? We could sabotage MyCompletNumber.equals to have it return a random value...

In Java, as in C++, or whatever language, the programmer must respect a minimum of semantics when writing code. This means implementing a "add" function that adds, and Cloneable method that clones, and a ++ operator than increments.

What's obfuscating anyway?

Now that we know that code can be sabotaged even through the pristine Java methods, we can ask ourselves about the real use of operator overloading in C++?

It is used to add syntactic sugar:

Iterators:

// Java iterators
it.moveNext() ;

// C++ iterators ;
++it ;
--it ;
it += 5 ;

Comparisons:

// Java comparison
boolean isEqual = myString.equals(myOtherString) ;

// C++ comparison
bool isEqual         = myGUID == myOtherGUID ;
bool isLesser        = myGUID <  myOtherGUID ;
bool isLesserOrEqual = myGUID <= myOtherGUID ;

Array accessors and subscripting:

// Java container accessors
myArray[25] ;
myVector.elementAt(25) ;
myMap.get("25") ;
my2DContainer.get(25, 42) ;

// C++ container accessors
myArray[25] ;
myVector[25] ;
myMap["25"] ;
my2DContainer(25, 42) ;

Advanced types:

// Java fictious advanced types
myMatrix = Matrix.multiply(myMatrixA, myMatrixB) ;
myMatrix = Matrix.multiply(Matrix.substract(myMatrixA, myMatrixB), Matrix.add(myMatrixC, myMatrixD)) ;

// C++ fictious advanced types
myMatrix = myMatrixA * myMatrixB ;
myMatrix = (myMatrixA - myMatrixB) * (myMatrixC + myMatrixD) ;

Functors:

// Java Functors ???
myFunctorObject.exec("Hello World", 42) ;

// C++ Functors
myFunctorObject("Hello World", 42) ;

String concatenation:

// Java concatenation
myStringBuffer.append("Hello ").append(25).append(" World") ;

// C++ stream handling
myStringStream   << "Hello " << 25 << " World" ;
myFileStream     << "Hello " << 25 << " World" ;
myOutputStream   << "Hello " << 25 << " World" ;
myNetworkStream  << "Hello " << 25 << " World" ;
myAnythingThatDerivesFromOStream << "Hello " << 25 << " World" ;

Ok, In Java you can use MyString = "Hello " + 25 + " World"... But, wait a second: This is operator overloading, isn't it? Isn't it cheating???

:-D

All in all, I wonder which language is really the less easy to read, when used by programmers (and not saboteurs, which seem to be quite numerous, according to the "operator overloading is evil" crowd).

The truth is that, like the toString(), clone(), equals() methods are for Java, C++ operator overloading is so much part of C++ that it becomes as natural as the original C operators, or the before mentioned Java methods.

In C, << and >> are used for bitwise operations. In C++, when used on streams, they are used to write or read. And in C++, you'll use more often the stream version than the bitwise version. The operator < and == is used implicitly by STL associative containers. supbscript [] is used on containers. function operator () is used for... functors. Etc. etc..

Combined with template programming, operator overloading becomes a well known design pattern. In fact, you cannot go very far in STL without using overloaded operators, and overloading operators for your own class.

Now, operator overloading should not be abused

Operator overloading should strive to respect the semantics of the operator. Do not substract in a + operator (as in "do not substract in a add function").

Cast overloadings are very dangerous because they can lead to ambiguities. So they should really be reserved for well defined cases. As for && and ||, do not ever overload them as the result will be surprising.

Why it is not possible in Java?

Because Java objects are used through references (i.e. nullable pointers without arithmetics). Thus, the operator = and == are already used to assign or compare references, not the values referenced.

Only concrete types would benefit from operator overloading, but Java's concrete types are primitives, and already have their operators assigned (like C++ operators for built-in types). .

But they do it in C#!!!

Yeah...

This difference between Java and C# never fails to amuse me. Apparently, the C# folks, with their "every primitive is a struct, and a struct derives from Object", got it right at first try.

paercebal
This is an excellent answer. I disagree with it, but it's still an excellent answer.I think the problems that are possible with bad overloads exceed the value of the good overloads.
Douglas Leeder
subtract, not substract
Eric
I guess you forgot an "append" at the Java example of string concatenation. Also forgot one or two spaces inside the strings. :)
Denilson Sá
@Denilson Sá: I corrected the code as you suggested. Thanks for your comment... ^_^ ...
paercebal
A: 

Well, chances are, if it's an object your looking at then its using the overload in C++/#. You could always wait a second or two with the mouse hovering over the operator for the popup info. Or, if you want to look at the overload function you'll have to scroll WAY OVER to the class view and double click on it.... whew, and I thought the treadmill was hard.

Facts:

When used correctly operator overloading and pointers rule!

The above fact is the reason that many good programmers stick with the C variations..

Obfuscate? hmmmpf.

A: 

Saying that operator overloading leads to logical errors of type that operator does not match the operation logic, it's like saying nothing. The same type of error will occur if function name is inappropriate for operation logic - so what's the solution: drop the ability of function usage!? This is a comical answer - "Inappropriate for operation logic", every parameter name, every class, function or whatever can be logicly inappropriate. I think that this option should be available in respectable programing language, and those that think that it's unsafe - hey no bothy says you have to use it. Lets take the C#. They drooped the pointers but hey - there is 'unsafe code' statement - program as you like on your own risk.

Kvant
A: 

Some people say that operator overloading in Java would lead to obsfuscation. Have those people ever stopped to look at some Java code doing some basic maths like increasing a financial value by a percentage using BigDecimal ? .... the verbosity of such an exercise becomes its own demonstration of obsfuscation. Ironically, adding operator overloading to Java would allow us to create our own Currency class which would make such mathematical code elegant and simple (less obsfuscated).

Golfman
A: 

@paercebal Excellent answer, i agree it is up to developer to use operator overloading gracefully and it would have been a great feature to have in java, in my opinion. You can always use another language that targets the JVM and write say a class in that language that supports operator overloading i.e jython, groovy and then consume that class from java. I can't think of another way to do operator overloading, maybe hack the bytecode ? of a class.

Here are some links that describe how to do operator overloading in groovy.

http://groovy.codehaus.org/Operator+Overloading

{edit 23-09-2010} I guess it won't be possible by hacking the bytecode, because operator overloading is essentially syntactic sugar for the languages that use this feature i.e obj1 + obj2 calls a method obj1.add(obj2) under the scenes and the compiled bytecode will contain the syntax with the add() method.

A special case is the String object which overloads the (+) operator and uses the above pattern, a StringBuilder is created under the scenes(in most cases) and it's append() method is called and then it's toString() method to return the concatenation of the Strings. So the compiler treats the String object as a special case. i.e


// s4 = s1+s2+s3 translates to
s4 = new StringBuilder().append(s1).append(s2).append(s3).toString();
 
Philippos Papadatos