如何使用具有多个实现类的java接口

时间:2009-10-02 21:27:01

标签: java interface oop

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中的实现。这个问题是我最终添加了相当多的锅炉板代码。 (如果其他人实现了接口,他们必须实现新方法,以及任何测试)

考虑到FooSpecificFoo是相关的,似乎我应该有另一个选项。想法?

更多信息:

好吧,我实际上已经工作了一段时间来尝试简化问题。随着我添加更多细节,复杂性上升了很多。但无论如何......我想我可以解释一下。

基本上,我正在使用命令模式编写一个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的废话只会在基础问题上泛滥,这就是为什么我尽力简化它。

4 个答案:

答案 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);
        }
}