The Manifold extension dependency plugs into Java to provide seamless operator overloading capability. You can type-safely provide arithmetic, relational, and unit operators for any class by implementing one or more predefined operator methods. You can implement operator methods directly in your class or use extension methods to implement operators for classes you don’t otherwise control. For example, using extension methods Manifold provides operator implementations for BigDecimal
so you can write code like this:
BigDecimal result = bigValue1 + bigValue2;
With unit expressions it gets even better where you can work with BigDecimal
and other types more easily:
BigDecimal result = 3.14bd * 10.75bd; // `bd` makes BigDecimals
Precise measurements via units and operator overloading:
Length distance = 65 mph * 3.2 hr; HeatCapacity kBoltzmann = 1.380649e-23r J/dK;
Arithmetic and Negation Operators
Any type can support arithmetic operators by implementing one or more of the following operator methods:
Arithmetic
Operation | Method |
---|---|
a + b |
a.plus(b) |
a - b |
a.minus(b) |
a * b |
a.times(b) |
a / b |
a.div(b) |
a % b |
a.rem(b) |
Negation
Operation | Method |
---|---|
-a |
a.unaryMinsu() |
Note operator methods do not belong to a class or interface you implement. Instead you implement them structurally simply by defining a method with the same signature. Note you can implement several different versions of the same method differing by parameter type.
Here’s a simple example demonstrating how to implement the +
operator:
public class Point { public final int x, y; public Point(int x, int y) {this.x = x; this.y = y;} public Point plus(Point that) { return new Point(x + that.x, y + that.y); } } var a = new Point(1, 2); var b = new Point(3, 4); var sum = a + b; // Point(4, 6)
Since operator methods are structural, you can define multiple plus()
methods:
public Point plus(int[] coord) { if(coord.length != 2) { throw new IllegalArgumentException(); } return new Point(x + coord[0], y + coord[1]); }
Relational Operators
You can implement relational operators using a combination of the ComparableUsing
and/or Comparable
interfaces.
manifold.ext.api.ComparableUsing
Relational operators can be implemented all together with the ComparableUsing
interface, which extends Comparable
to provide an operator-specific API.
boolean compareToUsing( T that, Operator op );
Where Operator
is an enum
which specifies constants for relational operators.
Operation | ComparableUsing Impl | Comparable Impl |
---|---|---|
a > b |
a.compareToUsing(b, GT) |
a.compareTo(b) > 0 |
a >= b |
a.compareToUsing(b, GE) |
a.compareTo(b) >= 0 |
a < b |
a.compareToUsing(b, LT) |
a.compareTo(b) < 0 |
a <= b |
a.compareToUsing(b, LE) |
a.compareTo(b) <= 0 |
ComparableUsing
provides a default implementation for compareToUsing()
that delegates to Comparable
‘s compareTo()
implementation for the >
, >=
, <
, <=
subset of relational operators. For the ==
and !=
subset ComparableUsing
delegates to the type’s equals()
method (more on equality later). This behavior is suitable for most types, so normally you only need to add ComparableUsing
to your type’s implements
or extends
clause and implement just Comparable
as you normally would. Thus adding relational operator support to the Point
example we have:
public class Point implements ComparableUsing { public final int x, y; public Point(int x, int y) {this.x = x; this.y = y;} public Point plus(Point that) { return new Point(x + that.x, y + that.y); } public int compareTo(Point that) { return x - that.x; } }
Now you can easily compare `Point` values like this:
if (pt1 >= pt2) ...
java.lang.Comparable
If you’re not interested in supporting ==
and !=
and your type implements the Comparable
interface, it automatically supports the >
, >=
, <
, <=
subset of relational operators. For example, both java.lang.String
and java.time.LocalDate
implement the compareTo()
method from Comparable
, which means they can be used in relational expressions:
String name1; String name2; ... if (name1 > name2) {...}
LocalDate date1; LocalDate date2; ... if (date1 > date2) {...}
SEE ALSO: Manifold: Java Access Control, Stop the Insanity!
Equality Operators
To implement the ==
and !=
subset of relational operators you must implement the ComparableUsing
interface. By default ComparableUsing
delegates to your type’s equals()
method, but you can easily override this behavior by overriding the equalityMode()
method in your CopmarableUsing
implementation. The EqualityMode
enum provides the available modes:
/** * The mode indicating the method used to implement {@code ==} and {@code !=} operators. */ enum EqualityMode { /** Use the {@code #compareTo()} method to implement `==` and `!=` */ CompareTo, /** Use the {@code equals()} method to implement `==` and `!=` (default) */ Equals, /** Use {@code identity} comparison for `==` and `!=`, note this is the same as Java's normal {@code ==} behavior } */ Identity }
Based on the EqualityMode
returned by your implementation of CompareToUsing#equalityMode()
, the ==
and !=
operators compile using the following methods:
Operation | Equals (default) |
CompareTo |
Identity |
---|---|---|---|
a == b |
a.equals(b) |
a.compareToUsing(b, EQ) |
a == b |
a != b |
!a.equals(b) |
a.compareToUsing(b, NE) |
a != b |
Note Manifold generates efficient, null-safe code for ==
and !=
. For example, a == b
using Equals
mode compiles as:
a == b || a != null && b != null && a.equals(b) If you need something more customized you can overridecompareToUsing()
with your own logic for any of the operators, including==
and!=
. To enable==
onPoint
more effectively, you can accept the default behavior ofComparableUsing
and implementequals()
:
public boolean equals(Object that) { return this == that || that != null && getClass() == that.getClass() && x == ((Point)that).x && y == ((Point)that).y; }
Note always consider implementing
hashCode()
if you implementequals()
, otherwise your type may not function properly when used withMap
and other data structures:public int hashCode() { return Objects.hash(x, y); }
Sometimes it’s better to use the CompareTo
mode. For instance, the ==
and !=
implementations for Rational
, BigDecimal
, and BigInteger
use the CompareTo
mode because in those classes compareTo()
reflects equality in terms of the face value of the number they model e.g., 1.0 == 1.00, which is desirable behavior in many use-cases. In that case simply override equalityMode()
to return CompareTo
:
@Override public EqualityMode equalityMode() { return CompareTo; }
SEE ALSO: Manifold: Say Goodbye to Checked Exceptions
Unit Operators
Unit or “binding” operations are unique to the Manifold framework. They provide a powerfully concise syntax and can be applied to a wide range of applications. You implement the operator with the prefixBind()
and postfixBind()
methods:
Operation | Postfix Bind | Prefix Bind |
---|---|---|
a b |
b.postfixBind(a) |
a.prefixBind(b) |
If the type of a
implements R prefixBind(B)
where B
is assignable from the type of b
, then a b
compiles as the method call a.prefixBind(b)
having type R
. Otherwise, if the type of b
implements R postfixBind(A)
where A
is assignable from the type of a
, then a b
compiles as the method call b.postfixBind(a)
having type R
.
This feature enables expressions such as:
Mass m = 5 kg; Force f = 5 kg * 9.8 m/s/s; for (int i: 1 to 10) {...}
Read more about unit expressions.
Operators by Extension Methods
Using extension methods you can provide operator implementations for classes you don’t otherwise control. For instance, Manifold provides operator extensions for BigDecimal
and BigInteger
. These extensions are implemented in the manifold-science
dependency.
Here’s what the +
extension for BigDecimal
looks like:
@Extension public abstract class ManBigDecimalExt implements ComparableUsing { /** Supports binary operator {@code +} */ public static BigDecimal plus(@This BigDecimal thiz, BigDecimal that) { return thiz.add(that); } ... } ``` Now you can perform arithmetic and comparisons using operator expressions: ```java if (bd1 >= bd2) { BigDecimal result = bd1 + bd2; . . . }
Note the
manifold-science
andmanifold-collections
modules use operator overloading and unit expressions extensively.
The post Manifold: Operator Overloading for Java appeared first on JAXenter.
Source : JAXenter