使用常规父类型输入参数强制执行动态多态调用

时间:2012-01-03 23:20:37

标签: java dynamic polymorphism parent

我正在尝试使用多态来根据其类启用对象的不同处理,如下所示:

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 个答案:

答案 0 :(得分: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将对象作为参数,因此您可以通过在GeneralStuff中使用在特定子类中重写的getValue和getFormatString方法来避免这个问题。

答案 1 :(得分:2)

你实际上是在正确的轨道上,在这种情况下你确实需要使用反射。您正在寻找的是double dispatch,因为您希望在动态类型的stuff参数上完成调度。

这种动态切换类型并不像您想象的那么罕见。请参阅this javaworld tipe,其中反映了visitor pattern

答案 2 :(得分:1)

编译器有充分理由抱怨。无法保证您的GeneralStuff对象是IntStuffDoubleStuff。它可以是简单的GeneralStuffGeneralStuff的任何其他扩展名,这也是您process方法中未使用instanceof覆盖的情况(除非返回空String是理想的行为。)

是否无法将process方法移至GeneralStuff类并在扩展中覆盖它?

另一种可能的解决方案是使用一种复合ProcessStuff类,在其中插入IntStuffProcessDoubleStuffProcess,...实例。这些实例中的每一个仍将具有instanceof检查以确定它们是否可以处理传递给它们的GeneralStuff对象,但这至少比一个大的构造实例更具可伸缩性/可维护性

答案 3 :(得分: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());
    }
}

答案 4 :(得分:0)

将GeneralStuff定义为抽象类,并使用doProcess方法(abstract)填充继承类。这样就可以避免instanceof值等所有问题。或者你可以按照βнɛƨнǤʋяʋʋɢ的建议去做,但是你仍然需要为每个特定的类定义一个重载,而在我的中你只需要直接调用它。

所以我的建议是:     公共抽象类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());
    }
}