public interface Foo {
}
public class SpecificFoo implements Foo {
}
public interface SomeInterface {
void thisMethod(Foo someKindOfFoo);
}
public class SomeClass implements SomeInterface {
public void thisMethod(Foo someKindOfFoo) {
// calling code goes into this function
System.out.println("Dont go here please");
}
public void thisMethod(SpecificFoo specificFoo) {
// not into this function
System.out.println("Go here please");
}
}
public class SomeOlderClass {
public SomeOlderClass( SomeInterface inInterface ) {
SpecificFoo myFoo = new SpecificFoo();
inInterface.thisMethod(myFoo);
}
}
调用代码:
SomeClass myClass = new SomeClass();
SomeOlderClass olderClass = new SomeOlderClass(myClass);
我有一个几个类调用的接口(SomeInterface
)(例如SomeOlderClass
)。我有一个实现接口的类,但我想对传递给泛型接口的特定实现进行类型安全操作。
如上面的代码所示,我真的希望能够创建另一个与传入接口的特定类型相匹配的方法。这不起作用。我认为这是因为调用代码只知道接口,而不是使用更具体的方法实现(即使SpecificFoo implements Foo
)
那么我怎样才能以最优雅的方式做到这一点?我可以通过在实现接口(SomeClass
)的类中添加if语句来获取代码:
public void thisMethod(Foo someKindOfFoo) {
// calling code goes into this function
if ( someKindOfFoo.getClass().equals(SpecificFoo.class) )
thisMethod(SpecificFoo.class.cast(someKindOfFoo));
else
System.out.println("Dont go here please");
}
然而,这并不优雅,因为我每次添加一种新的Foo时都要添加if语句。我可能会忘记这样做。
另一个选项是将SpecificFoo
添加到SomeInterface
,让编译器进行整理提醒我需要SomeClass
中的实现。这个问题是我最终添加了相当多的锅炉板代码。 (如果其他人实现了接口,他们必须实现新方法,以及任何测试)
考虑到Foo
和SpecificFoo
是相关的,似乎我应该有另一个选项。想法?
更多信息:
好吧,我实际上已经工作了一段时间来尝试简化问题。随着我添加更多细节,复杂性上升了很多。但无论如何......我想我可以解释一下。
基本上,我正在使用命令模式编写一个GWT Web应用程序RPC servlet,正如Ray Ryan在talk
中解释的那样谷歌代码上有几种实现方式,但其中很多都遇到了这种继承问题。我认为这是GWT-RPC代码bugreport中的一个错误,因为我在进一步实施时发现了类似的问题纯粹在客户端,并且在托管模式下。 (即所有java,没有gwt javascript疯狂)。
所以我将基本思想抽象为原始java命令行案例,并看到了同样的问题,如上所述。
如果您按照Ray Ryan所讨论的内容进行操作,Foo
是一个操作,SpecificFoo
是我想要调用的特定操作。 SomeInterface
是客户端RPC服务,SomeClass
是服务器端RPC类。 SomeOlderClass
是一种rpc服务,可以了解缓存等等。
明显,对吗?正如我所说的那样,我认为所有GWT RPC的废话只会在基础问题上泛滥,这就是为什么我尽力简化它。
答案 0 :(得分:3)
如果需要在运行时找出对象的实际类型,那么设计很可能是错误的。这至少违反了Open Closed Principle and Dependency Inversion Principle。
(因为Java没有multiple dispatch,所以thisMethod(Foo)
将被调用而不是thisMethod(SpecificFoo)
。Double dispatch可用于解决语言的限制,但可能会仍然是潜伏在那里的一些设计问题...)
请提供有关您要完成的内容的更多信息。现在这个问题没有提供足够的信息来提出正确的设计。
通用解决方案是,由于操作取决于Foo
的运行时类型,因此该方法应该是Foo
的一部分,以便其实现可以根据Foo
的类型而变化。因此,您的示例将更改为以下内容(可能会将SomeInterface
或其他参数添加到thisMethod()
)。
public interface Foo {
void thisMethod();
}
public class SpecificFoo implements Foo {
public void thisMethod() {
System.out.println("Go here please");
}
}
答案 1 :(得分:2)
尝试使用双重调度:将方法添加到Foo
调用的SomeClass#thisMethod
接口。然后将代码放在此方法的实现中。
public interface Foo {
public void thatMethod(SomeClass a);
public void thatMethod(SomeOlderClass a);
}
public class SomeClass implements SomeInterface {
public void thisMethod(Foo someKindOfFoo) {
someKindOfFoo.thatMethod(this);
}
}
答案 2 :(得分:2)
抱歉,我发现问题描述太抽象了,无法提出建议。您显然存在设计问题,因为您通常不需要检查界面类型。我会试一试......首先,我需要让你的问题更具体,让我的小脑子能够理解。而不是Foos,鸟怎么样?
public interface Bird {
}
public class Ostrich implements Bird {
}
public interface BirdManager {
void fly(Bird bird);
}
public class AdvancedBirdManager implements BirdManager {
public void fly(Bird bird) {
System.out.println("I am in the air. Yay!");
}
public void fly(Ostrich ostrich) {
System.out.println("Sigh... I can't fly.");
}
}
public class ZooSimulation {
public ZooSimulation(BirdManager birdManager) {
Ostrich ostrich = new Ostrich();
birdManager.fly(ostrich);
}
}
public static void main(String[] args) {
AdvancedBirdManager advancedBirdManager = new AdvancedBirdManager();
ZooSimulation zooSimulation = new ZooSimulation(advancedBirdManager);
}
在这里,鸵鸟将宣布“我在空中。是的!”这不是我们想要的。
好吧,所以,忽略了我在这里失败基本OO的事实,问题是BirdManager将寻找与传入的类型匹配的最不具体的方法。所以无论我给哪种鸟类它,它将始终匹配fly(Bird)
。我们可以在其中放置一些if
支票,但是当您添加更多类型的鸟类时,您的设计会进一步降级。这是一个棘手的部分 - 我不知道这是否在您的问题的背景下是有意义的,但考虑这种重构,我将逻辑从经理移动到鸟:
public interface Bird {
void fly();
}
public class BasicBird implements Bird {
public void fly() {
System.out.println("I am in the air. Yay!");
}
}
public class Ostrich implements Bird {
public void fly() {
System.out.println("Sigh... I can't fly.");
}
}
public interface BirdManager {
void fly(Bird bird);
}
public class AdvancedBirdManager implements BirdManager {
public void fly(Bird bird) {
bird.fly();
}
}
public class ZooSimulation {
public ZooSimulation(BirdManager birdManager) {
Ostrich ostrich = new Ostrich();
birdManager.fly(ostrich);
}
}
public static void main(String[] args) {
AdvancedBirdManager advancedBirdManager = new AdvancedBirdManager();
ZooSimulation zooSimulation = new ZooSimulation(advancedBirdManager);
}
我们的鸵鸟现在说正确的事情,鸟经理仍然把它当成一只鸟。再次,糟糕的OO(Ostriches不应该有fly()
方法),但它说明了我的想法。
答案 3 :(得分:0)
只要Foo
的实现没有太多,我就会在SomeInterface
中为Foo
的每个子类声明一个抽象方法,并且有一个抽象类前向调用为最常规类型定义的默认方法:
public interface Foo {
}
public class SpecificFoo implements Foo {
}
public interface SomeInterface {
void thisMethod(Foo someKindOfFoo);
void thisMethod(SpecificFoo specificFoo);
void thisMethod(OtherSpecificFoo otherSpecificFoo);
}
public abstract class AbstractSomeInterface {
public void thisMethod(Foo wrongFoo) {
throw new IllegalArgumentException("Wrong kind of Foo!");
}
public void thisMethod(SpecificFoo specificFoo) {
this.thisMethod((Foo) specificFoo);
}
public void thisMethod(OtherSpecificFoo otherSpecificFoo) {
this.thisMethod((Foo) specificFoo);
}
}
public class SomeClass extends AbstractSomeInterface {
public void thisMethod(SpecificFoo specificFoo) {
// calling code goes into this function
System.out.println("Go here please");
}
}
public class SomeOlderClass {
public SomeOlderClass( SomeInterface inInterface ) {
SpecificFoo myFoo = new SpecificFoo();
inInterface.thisMethod(myFoo);
}
}