我正在尝试使用多态来根据其类启用对象的不同处理,如下所示:
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而使我感到厌烦。)
有没有办法可以更好地强制执行多态调用?
提前感谢您的帮助。
答案 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
对象是IntStuff
或DoubleStuff
。它可以是简单的GeneralStuff
或GeneralStuff
的任何其他扩展名,这也是您process
方法中未使用instanceof
覆盖的情况(除非返回空String
是理想的行为。)
是否无法将process
方法移至GeneralStuff
类并在扩展中覆盖它?
另一种可能的解决方案是使用一种复合ProcessStuff
类,在其中插入IntStuffProcess
,DoubleStuffProcess
,...实例。这些实例中的每一个仍将具有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());
}
}