0

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?

Alxe
  • 113
  • 1
  • 2
    You could preserve backwards compatibility by compiling operators to functions. That's all an operator really is anyway: a function. I don't think it would even have to be limited, nor would you necessarily need interfaces. – Robert Harvey May 19 '16 at 17:08
  • this is why parenthesis-free (well mostly) languages with infix functions (like haskell) are such a joy! + is simply a function of type `Num a => a -> a -> a` (`static int +(int a, int b)` in C-like syntax (although in haskell it's polymorphic)) – sara May 19 '16 at 20:20

2 Answers2

7

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.

Jörg W Mittag
  • 101,921
  • 24
  • 218
  • 318
  • 1
    "Make them methods" is how scala does custom operators – Daenyth May 19 '16 at 19:33
  • Well, actually, Scala's approach is even simpler: there are no operators. `foo bar baz` is simply an alternative way of writing `foo.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 May 19 '16 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 May 19 '16 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 May 19 '16 at 23:03
  • @Jules: Hopefully fail to compile :-D – Jörg W Mittag May 19 '16 at 23:28
  • I guess the title should have said without changing bytecode, making the bytecode executable on older Java Virtual Machines. Anyways, a good answer is to be accepted :) – Alxe May 23 '16 at 15:54
  • @Alxe: Like I said in the pen-ultimate paragraph: if you compile Java operators to JVML methods (e.g. compile Java `Money.+` into a JVM `$plus$` method), you don't have to change the bytecode and classfile format. After all, there are plenty of languages with compilers for the JVM which permit names that are illegal in JVM bytecode. E.g. in Ruby, `+@`, `foo!`, and `foo?` are valid names for methods, in Scheme, `foo-bar-baz` is a valid name, in Scala `:::` is a valid identifier for both methods and classes. Java has generics, inner classes, anonymous classes, lambdas, the JVM doesn't. – Jörg W Mittag May 23 '16 at 15:59
1

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.

Matthias Ronge
  • 507
  • 3
  • 11
  • 2
    Allowing 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 May 20 '16 at 09:20