2012-01-03 2 views
2

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

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).

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

Я мог бы, конечно, определить 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 таким образом,).

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

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

ответ

0

Может быть, это лучше перегрузили 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()); 
    } 
} 
0

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

So мое предложение было бы: общественный абстрактный класс GeneralStuff { 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()); 
    } 
} 
1

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

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

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

+0

В реальном приложении объекты параметров ('IntStuff',' DoubleStuff') упаковываются как экземпляры 'GeneralStuff' в объект' Bundle', который передается другому ('ProcessStuff') для обработки, где объекты параметров вытягиваются и выполняются разные действия по их фактическим типам. Таким образом, на самом деле у меня нет возможности предоставлять перегруженные методы процесса в 'ProcessStuff', которые вызываются напрямую (предложенный @ βнɛƨн Ǥʋяʋиɢ), и перемещение метода' process' в 'GeneralStuff', вероятно, также не является вариантом (предложенный вами и @codegrabber). –

3

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

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

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

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, которые переопределяются в определенных подклассах.

+0

Это работает, и это умный способ перенаправления на основе класса. «Карта» также может использоваться для прямого потока для разных методов в классе «ProcessStuff». Благодаря! –

2

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

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

+0

Отличные ссылки! Спасибо. –

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