Reputation: 420
I'm trying to implement a CAS like thing in Java, but I'm struggling with the types and method selection.
When I add the two subtypes, everything is fine. When I add a subtype with a supertype an infinite recursion occurs. When the supertype is casted down its fine again and when I add a supertype to a subtype the same recursion occurs again.
Can someone explain what's happening here and what I'm doing wrong?
public class MathMain{
public static void main(String[] args){
Constant constant = new Constant(1);
constant.add(constant);
MathObject mathObject = (MathObject)constant;
constant.add(mathObject);
constant.add((Constant)mathObject);
mathObject.add(constant);
}
}
public abstract class MathObject{
public abstract MathObject add(MathObject addend);
public abstract MathObject substract(MathObject subtrahend);
}
public class Constant extends MathObject{
public final double c;
public Constant(double c){
this.c = c;
}
public MathObject add(MathObject that){
return that.add((Constant)this);
}
public MathObject substract(MathObject that){
return that.substract((Constant)this);
}
public Constant add(Constant addend){
return new Constant(c + addend.c);
}
public Constant substract(Constant subtrahend){
return new Constant(c - subtrahend.c);
}
}
Upvotes: 1
Views: 59
Reputation: 393801
The important thing to remember is that method overload resolution is determined at compile time while method overriding is determined at runtime.
mathObject.add(constant)
chooses the MathObject add(MathObject addend)
method, which is the only add
method available for MathObject
.
At runtime, since the runtime type of mathObject
is Constant
, MathObject add(MathObject that)
of Constant
is executed, and it calls that.add((Constant)this)
. Since the type of that
is MathObject
, again MathObject add(MathObject addend)
is chosen, which means your method is calling itself in an infinite recursion.
The only way your Constant add(Constant addend)
will be called by the that.add((Constant)this)
expression is if the compile time type of that
was Constant
, since MathObject
doesn't have an add
method that accepts a Constant
argument.
Now, for the cases that work :
constant.add(constant)
constant.add((Constant)mathObject)
both call Constant add(Constant addend)
directly, since the compile type of constant
is Constant
and the method with the more specific argument types is chosen.
I don't know if that's a good solution, but one way to get over the infinite recursion is to check the type of the argument :
public MathObject add(MathObject that){
if (that instanceof Constant)
return add((Constant) that);
else
that.add((Constant)this); // note that the casting here only makes
// sense if MathObject has an add method
// that takes a Constant. otherwise is makes
// no difference
}
Upvotes: 2