Recently, I've been learning a bit more of C++ and the dangers and uses of operator overloading, and the readability boost it provides to arithmetic types (like Complex numbers).
A while ago, I was playing around with Rust, and I really liked how they handled operator overloading, that is, via Traits in the std::ops
crate. So, having:
struct Fraction {
numerator: i32,
denominator: i32,
}
impl Add for Fraction { /* ... */ }
Ends up overloading the +
operator. This leads me to wonder the subject of the title...
Can Java implement a limited set of operator overloading via Interfaces? The compiler could check if the caller class implements the interface for a given operator, and change the operator to a method call (thus, it all being syntax sugar) or fail compiling otherwise.
Would this imply breaking changes for code compiled in older versions? Anyone know if this has been already discussed in mailings list, or a JEP proposed?
2 Answers 2
The answer to your question is rather simple: Backwards-compatibility means not changing the meaning of existing code. Since there is no existing code using user-defined operators, because user-defined operators do not exist, introducing them cannot possibly break backwards-compatibility.
Foo a = new Foo();
Foo b = new Foo();
Foo c = a + b;
Such code just can't be written today.
The simplest implementation of operator overloading would be to just make them methods. E.g.
class Fraction {
int numerator, denominator;
public Fraction *(Fraction other) {
return new Fraction(numerator * other.numerator, denominator * other.denominator);
}
}
Since those names are currently illegal, backwards-compatibility is preserved: there is no code currently which uses these names and whose meaning could change. If you implement a name-mangling scheme for those methods, you could even get by without changing the bytecode and classfile formats, so code that is compiled with a JavaXYZ compiler will still run on Java 8.
This is the simplest possible solution. If you want something more sophisticated, like defining the fixity, associativity and precedence of operators, you will have to do more work. In particular, parsing is (somewhat) complicated: you have to know whether ∪
is left-associatice or right-associative before you can parse a ∪ b ∪ c
correctly, for example. So you have to analyze the imported operator definitions before you can parse the code.
-
1"Make them methods" is how scala does custom operatorsDaenyth– Daenyth2016年05月19日 19:33:18 +00:00Commented May 19, 2016 at 19:33
-
Well, actually, Scala's approach is even simpler: there are no operators.
foo bar baz
is simply an alternative way of writingfoo.bar(baz)
. (This is only half-true: there is some support for operators in that for method names composed purely of operator characters, when called using operator notation, the first character of the method determines the precedence. Also, when the last character is:
, the call is interpreted to be right-associative.)Jörg W Mittag– Jörg W Mittag2016年05月19日 19:41:56 +00:00Commented May 19, 2016 at 19:41 -
Ruby would be a better comparison: Ruby has a fixed set of operators, with fixed precedence, fixity and associativity, and those operators can be implemented by defining methods with corresponding names (usually identical to the operator, except
+@
and-@
for unary+
and-
).Jörg W Mittag– Jörg W Mittag2016年05月19日 19:43:40 +00:00Commented May 19, 2016 at 19:43 -
I'm not sure definable fixity makes sense in an OO language.. What should
class A { public A operator+ (A a) { ... } left 5}; class B extends A { public A operator+ (A a) { ... } right 7 }; ... A a1 = new A(), a2 = new B(), a3 = new B(), a4 = a1 + a2 + a3;
do?Jules– Jules2016年05月19日 23:03:28 +00:00Commented May 19, 2016 at 23:03 -
@Jules: Hopefully fail to compile :-DJörg W Mittag– Jörg W Mittag2016年05月19日 23:28:20 +00:00Commented May 19, 2016 at 23:28
It would break existing code if you could override ==
comparison, or even =
assignment. ==
is used to compare the memory addresses of two objects, independent of what their equals()
method says. ==
is even true for two null
objects. I don’t want to imagine what happens if objects are allowed to override =
assignment operator.
-
2Allowing some operators to be overloaded does not necessarily mean allowing all operators to be overloaded. Most languages that do allow operator overloading do not allow assignment to be overloaded (e.g. Python, Ruby, Smalltalk, Scala, Haskell). C++ does allow to overload assignment, and so far it hasn't brought down the Apocalypse. The behavior of
==
in Java breaks OO in a deeply fundamental way, being able to override it to fix that would be a good thing IMO.Jörg W Mittag– Jörg W Mittag2016年05月20日 09:20:36 +00:00Commented May 20, 2016 at 9:20
Num a => a -> a -> a
(static int +(int a, int b)
in C-like syntax (although in haskell it's polymorphic))