2016-03-20 2 views
1

Я пытаюсь реализовать CAS как вещь на Java, но я борюсь с типами и выбором метода.Поведение странного типа

Когда я добавляю два подтипа, все в порядке. Когда я добавляю подтип с супертипом, возникает бесконечная рекурсия. Когда супертип снова отменяется, и когда я добавляю супертип к подтипу, повторяется повторная рекурсия.

Может кто-нибудь объяснить, что здесь происходит и что я делаю неправильно?

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

ответ

2

Важно помнить о том, что разрешение перегрузки метода определяется во время компиляции, в то время как метод наиважнейшая определяется во время выполнения.

mathObject.add(constant) 

выбирает MathObject add(MathObject addend) метод, который является единственным методом add доступны для MathObject.

Во время выполнения, так как тип времени выполнения mathObject является Constant, MathObject add(MathObject that) из Constant выполняется, и это вызывает that.add((Constant)this). Так как тип that равен MathObject, то выбирается MathObject add(MathObject addend), что означает, что ваш метод вызывает себя в бесконечной рекурсии.

Единственный путь ваш Constant add(Constant addend) будет называться выражением that.add((Constant)this), если компиляции типа время that был Constant, поскольку MathObject не имеет add метод, который принимает Constant аргумент.

Теперь, за исключением случаев, которые работают:

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

Спасибо за указание на это упущение :) Я «Мы подумали об обходном клонировании вызываемого абонента и вызове вызывающего абонента, но как бы это сделать для объектов, которые необходимы для сохранения исходного указателя, например Переменная? –

+0

Также почему Java не осознает, что 'MathObject add (MathObject)' 'возвращает that.add (this)' это на самом деле константа вместо объявленного MathObject, даже если метод явно находится в классе Constant, а не в классе MathObject? Кстати, обходной путь выше не работает ни по какой-то причине. –

+0

@ S.Klumpers аргумент 'that' может быть любым подклассом' MathObject'. А так как в базе 'MathObject' нет метода' add (Constant addend) ', он не может вызывать этот метод при написании' that.add (this) ', даже если тип выполнения' that' равен 'Constant'. – Eran

Смежные вопросы