Java 在简单工厂中使用泛型时,如何解决这个难题

Java 在简单工厂中使用泛型时,如何解决这个难题,java,generics,factory-pattern,design-principles,Java,Generics,Factory Pattern,Design Principles,我有一个Data类,它有几个子类,比如JSONData,XMLData,IntegerData。任务是处理不同类型的传入数据。因此,基于接口(而非实现)的程序,我创建了以下泛型接口,用于编译时类型检查: interface DataProcessor<T extends Data> { void process(T data); } 上述设计存在问题-无法直接调用数据处理器返回实例上的进程方法: Data data = jsonData; ProcessorFactory.

我有一个
Data
类,它有几个子类,比如
JSONData
XMLData
IntegerData
。任务是处理不同类型的传入数据。因此,基于接口(而非实现)的程序,我创建了以下泛型接口,用于编译时类型检查:

interface DataProcessor<T extends Data> {
    void process(T data);
}
上述设计存在问题-无法直接调用
数据处理器
返回实例上的
进程
方法:

Data data = jsonData;
ProcessorFactory.create().process(data);

问题:由于编译时键入检查,上面的代码出现编译错误,因为数据必须是
数据的具体子类
,如何解决此问题?或者是设计本身。糟糕?如果是这样的话,什么设计会更好?

这是Java的经典问题,因为它不支持双重分派。人们使用访问者模式来规避这个问题。在您的情况下,您可以在
Data
类中公开一个visit函数,该函数接受
DataProcessor
,并运行它的
process
方法。从本质上说,事情是相反的

像这样的

interface Data {

  ....
   void visit(DataProcessor processor);
}

Data d = JsonData;
d.visit(jsonDataProcessor processor);
JsonData的
visit
函数如下所示

void visit(JsonDataProcessor processor) {

    processor.process(this);
}

尽管设计模式很酷,但您在问题中报告的编译错误并不是由于缺少双重分派而导致的

获得编译错误的原因是,例如,通过声明this:
JSONDataProcessor实现了DataProcessor{…}
您已经声明了此方法:
void process(JSONData data)

您可能假设
意味着您可以将静态类型为
数据的对象实例传递到
无效进程(JSONData Data)
,因为毕竟
数据扩展了数据。但Java不是这样工作的

<> P>查看编译错误原因的一种方法是考虑一种方法声明,如:<代码>公共静态空隙main(字符串ARG){…}/<代码>。即使
String扩展了Object
,将静态类型为
Object
的引用传递到声明为
main(String)
的方法中也是非法的

如果您尝试将该方法调用为
main(newobject())
,则会得到与
DataProcessor
相同的编译错误。通过引入不必要的设计模式来纠正您所犯的错误,未免太过分了。在您的情况下和
main(String)
的情况下,最简单的更正是传入方法声明采用的类型

“如何解决此问题?”

在我看来,最简单的解决方案是使用您最初声明的方法。如果您的方法是以类似的方式实现的,那么我已经确认这是有效的

...
JSONData data = new JSONData( ... );
ProcessorFactory.create().process(data);
...
这也是(不需要设计模式)

DataProcessor>dProc=DataProcessor.Factory.create();
数据=新的JSONData(…);
dProc.过程(数据);
“设计本身是否糟糕?”

将一个设计称为“好”或“坏”是主观的。更客观的是问自己:设计是否正确?它是否正确地完成了你的计划?如果它完成了你的计划,那么它是正确的。如果没有,那么就回到白色的绘图板上

另一个设计选项是决定根本不使用泛型,也不使用设计模式。简单的方法可能就是你所需要的


您提到:“编程到接口”“。也许你所有的设计需求都是老式接口形式的简单的旧子类型多态性。泛型可能不是你想要做的最好的设计选择。

非常感谢。这正是我想知道的:)真遗憾,我知道访问者模式,但真的没有得到本质:)Avishebhattachary@Rui的代码“由于编译时键入检查…”而出现编译错误-这是由
ProcessorFactory.create().process(数据)导致的
问题中的代码行。具体的编译错误可能是:
不兼容的类型:无法将数据转换为捕获#1 of?
。您对编译错误报告的回答是:“…这是Java的典型问题,因为它不支持双重调度…”。。。“-你是说Java缺少双重分派会导致捕获转换相关的编译错误吗?你能解释一下原因吗?非常感谢你的回答。我确实从你的答案中学到了很多:)因为第一个答案被标记为正确,我还是投你的票谢谢@Rui!-“…我确实从你的答案中学到了很多:)“-嘿,我很高兴它对您有所帮助:)-“第一个答案被标记为正确…”-您是否知道:“?”特别是,您是否知道“您可以随时更改接受的答案,或者干脆取消接受答案”的部分“?我也很想知道与您最初的设计意图有多接近?TIA:)我检查了您的答案是否正确。但是@AvishekBhattacharya的答案确实很有价值,因为他提到了Java的单一分派,这是great@AvishekBhattacharya很抱歉,我将正确答案更改为重复数据消除,但我确实感谢您的支持非常感谢@Rui!我和其他人一样喜欢设计模式。但是如果你想做出“好”的设计选择,那么你必须抵制潜在的致命诱惑。Java警告访问者模式的“冗长、干扰或限制”。这是另一个有趣的最爱。
void visit(JsonDataProcessor processor) {

    processor.process(this);
}
...
JSONData data = new JSONData( ... );
ProcessorFactory.create().process(data);
...
DataProcessor< Data< ? > > dProc = DataProcessor.Factory.create( );
Data<String> data = new JSONData( ... );
dProc.process( data );