Обеспечение динамических полиморфных вызовов с помощью входных аргументов общего родительского типа - PullRequest
2 голосов
/ 04 января 2012

Я пытаюсь использовать полиморфизм для включения различной обработки объекта в зависимости от его класса, следующим образом:

public class GeneralStuff {
    private int ID;
}

public class IntStuff extends GeneralStuff {
    private int value;

    public void setValue(int v)
    {
        value = v;
    }

    public int getValue()
    {
        return value;
    }
}

public class DoubleStuff extends GeneralStuff {
    private double value;

    public void setValue(double v)
    {
        value = v;
    }

    public double getValue()
    {
        return value;
    }
}

public class ProcessStuff {

    public String process(GeneralStuff gS)
    {
        return doProcess(gS);
    }

    private String doProcess(IntStuff i)
    {
        return String.format("%d", i.getValue());
    }

    private String doProcess(DoubleStuff d)
    {
        return String.format("%f", d.getValue());
    }
}


public class Test {
    public static void main(String[] args)
    {
        IntStuff iS = new IntStuff();
        DoubleStuff dS = new DoubleStuff();
        ProcessStuff pS = new ProcessStuff();

        iS.setValue(5);
        dS.setValue(23.2);

        System.out.println(pS.process(iS));
        System.out.println(pS.process(dS));
    }
}

Это, однако, не работает, потому что при вызове doProcess(gS) ожидается метод с подписью doProcess(GeneralStuff gS).

Я знаю, что я мог бы просто иметь два открытых метода полиморфных процессов в классе ProcessStuff, но реальная ситуация не позволяет этого, потому что я работаю в рамках ограничений существующего библиотечного механизма; это просто надуманный пример для тестирования.

Я мог бы, конечно, определить process(GeneralStuff gS) как

public String process(GeneralStuff gS)
{
    if (gS instanceof IntStuff)
    {
        return doProcess((IntStuff) gS);
    }
    else if (gS instanceof DoubleStuff)
    {
        return doProcess((DoubleStuff) gS);
    }
    return "";
}

, что работает, но, похоже, мне не нужно было этого делать (плюс, полиция по программированию помешала бы мне использовать instanceof таким образом).

Есть ли способ, которым я могу лучше реализовать полиморфные вызовы?

Заранее спасибо за любую помощь.

Ответы [ 5 ]

3 голосов
/ 04 января 2012

Тип динамической отправки, который вы ищете, невозможен в Java без использования отражения. Java выполняет связывание во время компиляции на основе объявленного типа (поэтому, даже если метод перегружен, фактический вызванный метод основан на объявленном типе переменной, а не типе времени выполнения).

Таким образом, вам остается либо использовать instanceof, как вы предлагаете, либо использовать рефлексию, либо помещать методы процесса в сами объекты (что является «нелепым» способом сделать это, но часто не подходит или не рекомендуется).

Одной из возможных альтернатив является создание карты обработки объектов по классам, например:

Map<Class<? extends GeneralStuff>,Processor> processors;

public String process(GeneralStuff stuff)
{
  Processor processor = processors.get(stuff.getClass());
  if (processor != null)
  {
    return processor.process(stuff);
  }
}

public interface Processor
{
  public String process(GeneralStuff stuff);
}

public class IntegerProcessor implements Processor
{
  public String process(GeneralStuff stuff)
  {
    return String.format("%i",((IntegerStuff) stuff).getValue());
  }
}

Однако, для вашего конкретного примера, String.format принимает объекты в качестве параметров, поэтому вы можете избежать всей этой проблемы, используя методы getValue и getFormatString в GeneralStuff, которые переопределяются в определенных подклассах.

2 голосов
/ 04 января 2012

Вы на правильном пути, вам действительно нужно использовать отражение в этом случае.То, что вы ищете, это двойная отправка , потому что вы хотите, чтобы отправка выполнялась для динамического типа параметра вещи.

Этот тип динамического включения не такой редкий, как вы думаете.См., Например, этот javaworld tipe , который отражает шаблон посетителя

1 голос
/ 04 января 2012

Компилятор жалуется не зря. Нет никакой гарантии, что ваш GeneralStuff объект является IntStuff или DoubleStuff. Это может быть простое GeneralStuff или любое другое расширение GeneralStuff, которое вы также не рассмотрели в своем методе process с instanceof (если только возврат пустого String не был желаемым поведением) .

Разве нельзя переместить этот метод process в класс GeneralStuff и переопределить его в расширениях?

Другое возможное решение - иметь своего рода составной класс ProcessStuff, в который вы подключаете экземпляр IntStuffProcess, DoubleStuffProcess, .... У каждого из этих экземпляров все еще будет проверка экземпляра, чтобы решить, могут ли они обрабатывать переданный им объект GeneralStuff, но это, по крайней мере, более масштабируемо / поддерживаемо, чем один большой экземпляр экземпляра

0 голосов
/ 04 января 2012

Определите GeneralStuff как абстрактный класс с помощью метода doProcess (abstract), который заполняется в наследующих классах.Таким образом вы избежите всех проблем с значениями instanceof и тому подобным.Или вы можете сделать то, что предлагает βнɛƨн Ǥʋяʋиɢ, но тогда вам все равно придется определить перегрузку для каждого конкретного класса, тогда как в моем случае вы просто вызываете его напрямую.

Поэтому мое предложение будет следующим: public abstract classGeneralStuff {private int ID;

    public abstract String process();
}

public class IntStuff extends GeneralStuff {
    private int value;

    public void setValue(int v)
    {
        value = v;
    }

    public int getValue()
    {
        return value;
    }

    @override
    public String process(){
        return String.format("%d", getValue());
    }
}

public class DoubleStuff extends GeneralStuff {
    private double value;

    public void setValue(double v)
    {
        value = v;
    }

    public double getValue()
    {
        return value;
    }

    @override
    public String process(){
        return String.format("%f", getValue());
    }
}

public class Test {
    public static void main(String[] args)
    {
        IntStuff iS = new IntStuff();
        DoubleStuff dS = new DoubleStuff();
        ProcessStuff pS = new ProcessStuff();

        iS.setValue(5);
        dS.setValue(23.2);

        System.out.println(iS.process());
        System.out.println(dS.process());
    }
}
0 голосов
/ 04 января 2012

Возможно, лучше перегрузить метод process в ProcessStuff:

public class ProcessStuff {

    private String process(IntStuff i) {
        return String.format("%d", i.getValue());
    }

    private String process(DoubleStuff d) {
        return String.format("%f", d.getValue());
    }
}
...