# Community

Blog Events Webinars Tutorials Forum
×

By Mi Zhou (Zhiye)

Operator overloading aims to redefine an operator that has been defined and has certain functions to complete more detailed and specific operations and other functions. From an object-oriented perspective, it means an operator can be defined as a method of a class, so the function of the operator can be used to represent a certain behavior of the object.

Let's consider the realization of such a function. The complete square difference formula (a^2 + 2ab + b^2) is implemented by `BigInteger`.

``private static final BigInteger BI_2 = BigInteger.valueOf(2);``

Standard Syntax:

``BigInteger res = a.multiply(a).subtract(BI_2.multiply(a).multiply(b)).add(b.multiply(b));``

If you can overload the `*`, `+`, and `-` operators in Java, you can write the following code:

``BigInteger res = a * a - BI_2 * a * b + b * b;``

There are at least two benefits to being able to perform operator overloading for numeric operations of non-primitive types.

1. The code is simpler to write and less error-prone.
2. The code is easier to read without many parentheses.

The implementation of operator overloading in Java still uses Manifold. Manifold allows you to overload Java operators in various scenarios, such as arithmetic operators (including `+`,`-`, `*`, `/`, and `%`), comparison operators (`>`, `>=`, `<`, `<=`, `==`, and `!=`), and index operators (`[]`). Please see Java's Missing Feature: Extension Methods for more information about the integration of Manifold.

### Arithmetic Operator

Manifold is a function that maps each overload of an arithmetic operator to a specific name. For example, if you define a `plus(B)` method in `class A`, that class can be called using `a + b` instead of `a.plus(b)`. The following chart describes the mappings:

 Operator Method Call c = a + b c = a.plus(b) c = a - b c = a.minus(b) c = a * b c = a.times(b) c = a / b c = a.div(b) c = a % b c = a.rem(b)

Those familiar with Kotlin should know that this is an imitation of Kotlin's operator overloading.

Let's define a numeric `Num` to facilitate illustration.

``````public class Num {

private final int v;

public Num(int v) {
this.v = v;
}

public Num plus(Num that) {
return new Num(this.v + that.v);
}

public Num minus(Num that) {
return new Num(this.v - that.v);
}

public Num times(Num that) {
return new Num(this.v * that.v);
}
}``````

For the following code:

``````Num a = new Num(1);
Num b = new Num(2);

Num c = a + b - a;``````

After Manifold processing at compile time, it becomes:

Operators have precedence on the mathematical operation, and Manifold supports it. So, for code like this:

``Num c = a + a * b - b;``

After Manifold processing, it is:

Since Java supports method overloading, you can receive multiple types of parameters for the `plus` method.

``````public class Num {

...

public Num plus(Num that) {
return new Num(this.v + that.v);
}

public Num plus(int i) {
return new Num(v + i);
}
}``````

This enhances the ability to overload operators.

``Num c = a + 1 + b;``

After Manifold processing:

Note: Since `+` and `*` satisfy the commutative law, `a + b` will first look for the conforming `plus` method in object A. If it exists in A, `a.plus(b)` will be executed. If it does not exist in a and the conforming `plus` method exists in b, `b.plus(a)` is executed. `a * b` is the same.

Java supports the compound assignment of values in primitive types (such as `+=` and `-=`). Manifold also supports this.

 Operator Method Call a += b a = a.plus(b) a -= b a = a.minus(b) a *= b a = a.times(b) a /= b a = a.div(b) a %= b a = a.rem(b)

What if it is an existing library and cannot add these methods to its classes? Don't forget that Manifold supports extension methods.

### Comparison Operators

For non-primitive types of Java objects, we use `Comparable<T>` to compare sizes. If your object implements Comparable, Manifold gives you the overload of the four comparison operators `>`, `>=`, `<`, and `<=`.

 Operator Method Call a > b a.compareTo(b) > 0 a >= b a.compareTo(b) >= 0 a < b a.compareTo(b) < 0 a <= b a.compareTo(b) <= 0

We let `Num` achieve `Comparable<Num>`.

``````public class Num implements Comparable<Num> {

...

@Override
public int compareTo(Num that) {
return this.v - that.v;
}
}``````

So, for code like this:

``````Num a = new Num(1);
Num b = new Num(2);

if (a > b) {
System.out.println("a > b");
}

if (a < b) {
System.out.println("a < b");
}``````

Run the code, and it will output `a < b` since after being processed by Manifold, the code will become:

You may ask about `==` and `!=`. Does Manifold support them? Yes. Manifold provides a new interface (`ComparableUsing<T>`) that allows you to implement the overloading of `==` and `!=`.

`ComparableUsing<T>` inherits `Comparable<T>` interface and adds two methods: `compareToUsing` and `equalityMode`. View the default implementation of `comparableUsing`:

The overloading of the four operators `>`, `>=`, `<`, and `<=` uses `compareTo` of `Comparable<T>` to implement it. For `==` and `!=`, they are based on the return value of the `equalityMode` method to choose which implementation to use.

• If it is `EqualityMode.CompareTo`, the overloading of `==` and `!=` corresponds to the case where the return value of the `compareTo` method is 0 and non-0, respectively.
• If it is `EqualityMode.Equals`, the overloading of `==` and `!=` corresponds to the case where the return value of the `equals` method is `true` and `false`.
• If it is `EqualityMode.Identity`, Java's default implementation is used, such as whether the reference addresses of the comparison objects are the same.

The `equalityMode` default method returns a value of `EqualityMode.Equals`, which means Manifold uses `equals` methods by default to judge `==` and `!=`. You can implement your `compareUsing` methods directly and handle the comparison logic of various `Operator` without using Manifold's `equalityMode` logic.

Let's let `Num` implement the `ComparableUsing<Num>` interface and override `equals`.

``````public class Num implements ComparableUsing<Num> {

...

@Override
public int compareTo(Num that) {
return this.v - that.v;
}

@Override
public boolean equals(Object obj) {
if (this == obj) { return true; }

if (obj instanceof Num) {
Num that = (Num) obj;
return this.v == that.v;
}

return false;
}

@Override
public int hashCode() {
return Objects.hash(v);
}
}``````

At this time, `==` and `!=` are overloaded and use an implementation based on the `equals` method. So, for the following code:

``````Num a = new Num(1);
Num b = new Num(1);

if (a == b) {
System.out.println("a == b");
}

if (a != b) {
System.out.println("a != b");
}``````

Run the code, and it will print `a == b` since the code after Manifold processing becomes:

Amazing! We finally realized this. Let `==` and `!=` use the logic of the `equals` method to compare rather than using reference addresses.

You should have noticed that if a type T wants to implement `ComparableUsing<T>`, the `T` must be `Comparable<T>`. If you want to overload `==` and `!=` for `T`, the `T` is required to be comparable. Manifold does this instead of providing a separate interface for overloading `==` and `!=` because the author currently believes that using `==` and `!=` instead of `equals` does more harm than good. After all, using `equals` to compare whether two objects are equal is too popular in Java. So, the author of Manifold hopes we will only use `==` and `!=` for objects (such as values and quantifiers). Do not abuse it.

What if it is an existing library (such as `String` and `BigInteger`) that cannot directly add interface implementations to its classes? You can create an extension class for this class, let the extension class implement `ComparableUsing<T>`, and Manifold will handle it as the class has implemented `ComparableUsing<T>`. For example, Manifold for `BigInteger` extension class `ManBigIntegerExt` (located in the manifest-science library):

It provides a `compareUsing` implementation of custom logic in the form of extension methods.

Note: The extension class should be decorated with the `abstract` keyword at this time because it is not intended to implement the `ComparableUsing<T>` interface in a normal way. Alternatively, you can declare an extension class as an interface and inherit `ComparableUsing<T>` interface.

### Index Operators

Java supports index operators for arrays. For example, `nums[i]` access the element with the array index i, and `nums[i] = n` assign values to the position with the array index i. However, Java cannot support `List` and `Map`. So, here comes Manifold again.

 Operator Method Call c = a[b] c = a.get(b) a[b] = c a.set(b, c)

Since `java.util.List` has these two methods. With Manifold, you can write code like this:

`Map` only has the `get` methods and no `set` methods, so you can add a `set` to the `Map` extension class.

``````@Extension
public class MapExt {

public static <K, V> V set(@This Map<K, V> map, K key, V value) {
return map.put(key, value);
}
}``````

Then, we can write the code like this:

Amazing! It should be noted that Manifold has requirements for the `set` methods: the return value of the `set` method cannot be `void`, and it should return a value of the same type as the second parameter (usually the old value). The reason for this requirement is to be consistent with the index assignment expression of Java's array (if `set` returns `void`, the index assignment expression cannot be supported). In Java, you can assign a value like this:

``````int[] nums = {1, 2, 3};
int value = nums[0] = 10;``````

After the execution is completed, the num[0] and value will be 10. So, when we use index assignment expressions:

``````List<String> list = Arrays.asList("a", "b", "c");
String value = list[0] = "A";``````

After Manifold processing, the code becomes:

Thus, similar to the `T value = list[0] = obj` expression, the `value` after execution is not the return value of the `set` method but the rightmost value:

### Unit Operator

Manifold also provides an interesting feature: unit operator. As the name implies, we can provide the unit function in the code. For example, the following code:

Stunned? Me too. The `dt` is the unit. Look at the code after Manifold processing:

In other words, Manifold replaces `"xxx"dt` with `dt.postfixBind("xxx")`, and you can guess the code of the `DateTimeUnit` class:

``````public class DateTimeUnit {

private static final
DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

public LocalDateTime postfixBind(String value) {
return LocalDateTime.parse(value, FORMATTER);
}
}``````

`postfixBind` means this unit is a suffix unit, which is the `"xxx"dt`. `dt` is after xxx. Manifold also supports prefix units, which correspond to `prefixBind` methods, such as:

``````public class DateTimeUnit {

...

public LocalDateTime prefixBind(String value) {
return LocalDateTime.parse(value, FORMATTER);
}
}``````

After adding `prefixBind(String)`, you can define `LocalDateTime` as:

Amazing! With the unit function, we can make many practical literal functions. For example, define the unit of `BigInteger`.

``````public class BigIntegerUnit {

public BigInteger postfixBind(Integer value) {
return BigInteger.valueOf(value);
}

public BigInteger postfixBind(String value) {
return new BigInteger(value);
}
}``````

`auto` coupled with Manifold (similar to `var` provided by Java 10, but `auto` can also be used to define attributes):

Who would think you are using Java 8? Also, we can use `postfixBind` and `prefixBind` together, such as by providing the following class:

``````public class MapEntryBuilder {

public <K> EntryKey<K> postfixBind(K key) {
return new EntryKey<>(key);
}

public static class EntryKey<K> {

private final K key;

public EntryKey(K key) {
this.key = key;
}

public <V> Map.Entry<K, V> prefixBind(V value) {
return new AbstractMap.SimpleImmutableEntry<>(key, value);
}
}
}``````

Then, you can create `Map.Entry` this way (`EntryKey` is created through `to.postfixBind`, and then `Map.Entry` is created through `prefixBind` method of `EntryKey`.):

If we provide `Map` with the following static extension methods:

``````@Extension
public class MapExt {

@Extension
@SafeVarargs
public static <K, V> Map<K, V> of(Map.Entry<K, V>... entries) {
Map<K, V> map = new LinkedHashMap<>(entries.length);

for (Map.Entry<K, V> entry : entries) {
map.put(entry.getKey(), entry.getValue());
}

return Collections.unmodifiableMap(map);
}
}``````

Then, you can create `Map` like this:

## Suggestion

Java has always not supported operator overloading, but there must be a reason. As a language that previously focused on enterprise application development, operator overloading was unnecessary. However, with the development of hardware, we have seen more Java appear in the data science/high-performance computing fields, and Java has begun to provide value types: Project Valhalla. Perhaps, with the application of value types, there will be more calls to provide operator overloading in Java soon. Maybe it will be adopted by JCP.

Like extension methods, when adding an operator overload, we must ask ourselves whether this class has the function of the corresponding operator semantics and whether the code written with the operator will reduce readability.

0 2 1
Share on

# mizhou

2 posts | 0 followers

# mizhou

2 posts | 0 followers

# Related Products

• ## YiDA Low-code Development Platform

A low-code development platform to make work easier

• ## mPaaS

Help enterprises build high-quality, stable mobile apps

• ## Super App Solution for Telcos

Alibaba Cloud (in partnership with Whale Cloud) helps telcos build an all-in-one telecommunication and digital lifestyle platform based on DingTalk.