2016-03-20 28 views
1

Ich versuche, eine CAS-ähnliche Sache in Java zu implementieren, aber ich habe Probleme mit der Auswahl der Typen und Methoden.Seltsames Verhalten

Wenn ich die beiden Subtypen hinzufüge, ist alles in Ordnung. Wenn ich einen Subtyp mit einem Supertyp hinzufüge, tritt eine unendliche Rekursion auf. Wenn der Supertype wieder in Ordnung ist, ist es wieder in Ordnung, und wenn ich einen Supertyp zu einem Subtyp hinzufüge, tritt die gleiche Rekursion wieder auf.

Kann jemand erklären, was hier passiert und was ich falsch mache?

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

Antwort

2

Die wichtige Sache zu erinnern ist, dass -Methodenüberladung Auflösung bei der Kompilierung bestimmt wird, während Methode überwiegende zur Laufzeit bestimmt wird.

mathObject.add(constant) 

wählt die MathObject add(MathObject addend) Methode, die die einzige Methode zum addMathObject ist.

Zur Laufzeit, da der Laufzeittyp von mathObjectConstant ist, MathObject add(MathObject that) von Constant ausgeführt wird, und es ruft that.add((Constant)this). Da der Typ thatMathObject ist, wird wiederum MathObject add(MathObject addend) gewählt, was bedeutet, dass sich Ihre Methode in einer unendlichen Rekursion aufruft.

Der einzige Weg, um Ihr Constant add(Constant addend) wird von dem that.add((Constant)this) Ausdruck aufgerufen werden, wenn die Kompilierung Art von thatConstant war, da MathObject keine add Methode, die ein Constant Argument akzeptiert.

nun für die Fälle, die funktionieren:

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

beide Anruf Constant add(Constant addend) direkt, da der Compilertyp von constantConstant und das Verfahren mit den spezifischeren Argumenttypen gewählt ist.

Ich weiß nicht, ob das eine gute Lösung ist, sondern eine Möglichkeit, über die unendliche Rekursion zu bekommen, ist die Art des Arguments zu überprüfen:

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 
} 
+0

Danke für dieses Versehen des Hinweis auf :) I habe an eine Problemumgehung gedacht, den Angerufenen geklont und den Aufrufer zurückgerufen, aber wie würde ich das für Objekte machen, die den ursprünglichen Zeiger erhalten müssen, z eine Variable? –

+0

Auch warum Java nicht realisiert, dass 'MathObject add (MathObject)' 'return that.add (this)' Dies ist eigentlich eine Konstante anstelle der deklarierten MathObject, auch wenn die Methode offensichtlich in der Constant-Klasse und nicht der MathObject-Klasse befindet? Übrigens funktioniert die obige Problemumgehung aus irgendeinem Grund nicht. –

+0

@ S.Klumpers das Argument 'das' kann eine beliebige Unterklasse von' MathObject' sein. Und da die Basis 'MathObject' keine' add (Constant addend) 'Methode hat, kann sie diese Methode nicht aufrufen, wenn Sie' that.add (this) 'schreiben, selbst wenn der Laufzeittyp von' that' ist "Konstante". – Eran