I have two simple classes: Point
(which has an x and y) and Point3D
(which extends Point
and also has a z parameter). I want to override the + operator so that I can add two points. Because the + operator needs to return the class, the method signature for + in Point is different from the signature for + in Point3D, which is not allowed in Dart. I have tried it via an interface:
abstract class PointOperations<T extends Point> {
T operator +(T p);
}
and implement this in Point and Point3D as follows:
class Point implements PointOperations<Point> {
final double x, y;
Point(this.x, this.y);
@override
Point operator +(Point p) => Point(x + p.x, y + p.y);
}
class Point3D extends Point implements PointOperations<Point3D>{
final double z;
Point3D(double x, double y, this.z) : super(x, y);
@override
Point3D operator +(Point3D p) => Point3D(x + p.x, y + p.y, z + p.z);
}
I get the compile error The class 'Point3D' cannot implement both 'PointOperations<Point>' and 'PointOperations<Point3D>' because the type arguments are different.
I understand that error, but don't understand how I can accomplish what I want without resorting to methods with distinct names (e.g. Point addPoint(Point p)
and Point3D addPoint3D(Point3D p)
).
I can get this to work using named constructors:
Point.add(Point a, Point b) : x = a.x + b.x, y = a.y + b.y;
and
Point3D.add(Point3D a, Point3D b) : z = a.z + b.z, super(a.x + b.x, a.y + b.y);
but this is not as elegant and certainly is not operator overloading. Is there a way to accomplish that?
I think I figured it out. In Point
I define:
Point operator +(covariant Point p) => Point(x + p.x, y + p.y);
Defining the argument as a covariant
is what allows the subclass to then use the argument Point3D
without conflict. In Point3D
I define:
@override
Point3D operator +(Point3D p) => Point3D(x + p.x, y + p.y, z + p.z);
The return type works as it's a subclass of Point
.
Note it is possible to use the following alternative:
@override
Point3D operator +(Point p) => Point3D(x + p.x, y + p.y, z + (p as Point3D).z);
where requires you cast the Point p
to access z
, but which may be useful if one would want to add a Point
to a Point3D
(which would require extra type checks and is not what I am looking for, but one could imagine doing this and setting z
to 0 if p
is a Point
and not Point3D
).