2.8.2 Operator Overloading
Abstracts allow overloading of unary and binary operators by adding the @:op
metadata to class fields:
abstract MyAbstract(String) {
public inline function new(s:String) {
this = s;
}
@:op(A * B)
public function repeat(rhs:Int):MyAbstract {
var s:StringBuf = new StringBuf();
for (i in 0...rhs)
s.add(this);
return new MyAbstract(s.toString());
}
}
class Main {
static public function main() {
var a = new MyAbstract("foo");
trace(a * 3); // foofoofoo
}
}
By defining @:op(A * B)
, the function repeat
serves as operator method for the multiplication *
operator when the type of the left value is MyAbstract
and the type of the right value is Int
. The usage is shown in line 17, which turns into this when compiled to JavaScript:
console.log(_AbstractOperatorOverload.
MyAbstract_Impl_.repeat(a,3));
Similar to implicit casts with class fields, a call to the overload method is inserted where required.
The example repeat
function is not commutative: While MyAbstract * Int
works, Int * MyAbstract
does not. If this should be allowed as well, the @:commutative
metadata can be added. If it should work only for Int * MyAbstract
, but not for MyAbstract * Int
, the overload method can be made static, accepting Int
and MyAbstract
as first and second type respectively.
Overloading unary operators is analogous:
abstract MyAbstract(String) {
public inline function new(s:String) {
this = s;
}
@:op(++A) public function pre()
return "pre" + this;
@:op(A++) public function post()
return this + "post";
}
class Main {
static public function main() {
var a = new MyAbstract("foo");
trace(++a); // prefoo
trace(a++); // foopost
}
}
Both binary and unary operator overloads can return any type.
Exposing underlying type operations
It is also possible to omit the method body of a @:op
function, but only if the underlying type of the abstract allows the operation in question and if the resulting type can be assigned back to the abstract.
abstract MyAbstractInt(Int) from Int to Int {
// The following line exposes the (A > B) operation from the underlying Int
// type. Note that no function body is used:
@:op(A > B) static function gt( a:MyAbstractInt, b:MyAbstractInt ) : Bool;
}
class Main {
static function main() {
var a:MyAbstractInt = 42;
if(a > 0) trace('Works fine, > operation implemented!');
// The < operator is not implemented.
// This will cause an 'Cannot compare MyAbstractInt and Int' error:
if(a < 100) { }
}
}