奇怪的类型行为

Strange type behavior

我正在尝试在 Java 中实现类似 CAS 的东西,但我在类型和方法选择方面遇到困难。

当我添加两个子类型时,一切都很好。 当我添加具有超类型的子类型时,会发生无限递归。 当超类型再次向下转换时,当我将超类型添加到子类型时,同样的递归再次发生。

有人可以解释这里发生了什么以及我做错了什么吗?

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);
    }
}

要记住的重要一点是,方法重载决策是在编译时确定的,而方法覆盖是在运行时确定的。

mathObject.add(constant)

选择 MathObject add(MathObject addend) 方法,这是 MathObject 唯一可用的 add 方法。

在运行时,由于mathObject的运行时类型是ConstantConstantMathObject add(MathObject that)被执行,它调用了that.add((Constant)this)。由于 that 的类型是 MathObject,再次选择 MathObject add(MathObject addend),这意味着您的方法正在无限递归中调用自身。

Constant add(Constant addend) 将被 that.add((Constant)this) 表达式调用的唯一方法是 that 的编译时类型是 Constant,因为 MathObject没有接受 Constant 参数的 add 方法。

现在,对于有效的案例:

constant.add(constant)
constant.add((Constant)mathObject)

都直接调用Constant add(Constant addend),因为constant的编译类型是Constant并且选择了具有更具体参数类型的方法。

我不知道这是否是一个好的解决方案,但是克服无限递归的一种方法是检查参数的类型:

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
}