【问题标题】:Java - Dynamic Class Casting from Interface to ImplementationJava - 从接口到实现的动态类转换
【发布时间】:2011-04-22 01:30:18
【问题描述】:

我已阅读其他相关帖子,但仍不太确定如何使用 Java 进行动态转换,或者是否可以动态转换(接口到实现)。我的印象是我必须使用反射来做到这一点。

我正在处理的特定项目需要使用许多 instanceof 检查,而且在我看来,它有点失控,所以如果有任何想法/解决方案,我将不胜感激。

下面是我写的一个小例子,只是为了明确我想要做什么。如果您需要更多信息,请告诉我:

界面:

public interface IRobot {
    String getName();
}

实现:

public class RoboCop implements IRobot {
    String name = this.getClass()+this.getClass().getName();
    public RoboCop() {}
    public String getName() { return name; }
}

public class T1000 implements IRobot {
    String name = this.getClass()+this.getClass().getName();
    public T1000() {}
    public String getName() { return name; }
}

处理实现的类:

import java.util.LinkedList;
import java.util.List;
public class RobotFactory {

    public static void main(String[] args) {
        new RobotFactory();
    }

    public RobotFactory() {
        List<IRobot> robots = new LinkedList<IRobot>();
        robots.add( new RoboCop() );
        robots.add( new T1000() );
        System.out.println("Test 1 - Do not cast, and call deploy(robot)");
        for(IRobot robot : robots) {
            deploy(robot);  // deploy(Object robot) will be called for each..
        }
        System.out.println("Test 2 - use instanceof");
        for(IRobot robot : robots) { // use instanceof, works but can get messy
            if(robot instanceof RoboCop) {
                deploy((RoboCop)robot);
            }
            if(robot instanceof T1000) {
                deploy((T1000)robot);
            }
        }
        System.out.println("Test 3 - dynamically cast using reflection?");
        for(IRobot robot : robots) {
            //deploy((<Dynamic cast based on robot's type>)robot);  // <-- How to do this?
        }
    }

    public void deploy(RoboCop robot) {
        System.out.println("A RoboCop has been received... preparing for deployment.");
        // preparing for deployment
    }

    public void deploy(T1000 robot) {
        System.out.println("A T1000 has been received... preparing for deployment.");
        // preparing for deployment
    }

    public void deploy(Object robot) {
        System.out.println("An unknown robot has been received... Deactivating Robot");
        // deactivate
    }
}

输出:

[RoboCop@42e816, T1000@9304b1]
Test 1 - Do not cast, and call deploy(robot)
An unknown robot has been received... Deactivating Robot
An unknown robot has been received... Deactivating Robot
Test 2 - use instanceof
A RoboCop has been received... preparing for deployment.
A T1000 has been received... preparing for deployment.
Test 3 - dynamically cast using reflection?

所以,总结一下我的问题,在这种情况下,我怎样才能完全避免使用instanceof。谢谢。

【问题讨论】:

  • +1 用于酷机器人示例 :-)
  • 谢谢,我试着让阅读变得有点有趣,呵呵 :)
  • 不必使用 instanceof 或 cast 通常意味着您的设计存在问题。这并不总是正确的,但如果它经常发生,那么几乎可以保证那里有更好的设计。在这种情况下,为什么不在robot中部署一个方法——你使用的system.out.println绝对应该在robot内部。如果deploy不能是robot中的方法,那么它应该只调用robot接口中的方法(这样你只需要一个deploy方法)。
  • @Bill 是的,我承认,在这个例子中,现在很容易看出这一点。我不得不退出实际的部署代码,这并不那么明显(或者至少对我来说不是)。 Meriton 的Vistor pattern 是我最终诉诸并将申请的。

标签: java reflection dynamic casting


【解决方案1】:

您可以使用IRobot 的方法进行部署,或者使用visitor pattern。

不,反思不会让事情变得更容易。

【讨论】:

  • 是的,这也是我最初的心血来潮。如果可能,我通常会尽量避免反射
  • 实际上这在我的演示中工作得很好。我将在实际的产品代码上尝试它,它的复杂程度要高得多,并让你知道它是如何进行的。谢谢。
  • 太好了,先生,刚刚帮我删除了100行代码!
  • 还有一个附加说明。我的实际代码实际上使用了Abstract Class 所以在这种情况下不要填写抽象类中的deploy()方法的主体,而是将其设为抽象方法并在每个调用部署的类中定义它(即把它当作一个接口)
【解决方案2】:

Kent Beck 在他的书Test Driven Development 中说:任何时候使用运行时类型检查,多态性都会有所帮助。将 deploy() 方法放入您的界面并调用它。您将能够透明地对待所有机器人。

忘记反射,你只是想多了。记住您的基本面向对象原则。

【讨论】:

  • 对,我想我把它弄得太复杂了。同样,使用反射的想法是向我提出的一个建议。虽然,当不需要它时,我不是它的忠实粉丝。 :P
  • 真正让它变得困难的是尝试修改现有代码。尤其是当它的代码已经被许多人工作了很多年时......在当时发现“最好的事情”变得更加困难
  • 当然。那么你能做的最好的事情就是对一些图表进行逆向工程,编写一些测试,并使用自动重构工具(如果你使用的是 Java)。不过,这通常很不愉快。
【解决方案3】:

重载方法的分派是在编译时静态完成的,因此您的方法无法正常工作。这也是非常糟糕的设计。 getName() 方法是机器人类之间的唯一方法,实际上从未被调用,这让您感到奇怪吗?

您必须放弃重载的方法,而是使用您直接调用的机器人类中的方法的方法覆盖。即

public void deploy(IRobot robot) {
    System.out.println("A "+robot.getName()+" has been received..."
                        +" preparing for deployment.");
    // preparing for deployment
}

【讨论】:

  • 谢谢,一切都很有意义,演示对我有用。虽然我想确定我完全理解你所说的“方法覆盖方法”是什么意思,但你会说部署方法是在 RobotFactory 中定义的,对吗?以及应该对 IRobot Impelementations 进行哪些更改? (除了 getName 方法,我只是把它拍在那里进行演示)
  • @KennyCason:方法覆盖不是最好的词,因为您使用的是接口。你想要的是动态调度。当您有不同的类以不同的方式实现相同的方法(相同的名称,相同的参数)时,您就会明白这一点。当您调用此类方法时,将使用您调用它的对象的实际运行时类的实现。如果您使用覆盖(相同名称,不同参数),则不会发生这种情况,而是参考的编译时类型决定使用哪个实现。
  • 啊。知道了。这是一个重要的区别
  • “覆盖(同名,不同参数)” - 我假设你的意思是 over_loading_?
【解决方案4】:

您可以通过在 IRobot 接口和实现中移动 deploy 方法来避免使用 instanceof。

行为的解释是你的三个部署方法是三种不同的方法;具有不同签名的重载方法。在编译的时候就决定选择哪一个,而不是在运行时根据真实的类...

【讨论】:

  • “在编译时,根据实际类确定选择哪一个,而不是在运行时” - 谢谢,我不知道。
【解决方案5】:

您可以使用Factory Method Pattern,而不是使用instanceof

工厂方法的定义...

像其他创造模式一样,它 处理创建问题 未指定的对象(产品) 对象的确切类别 已创建。

您将需要一个 RobotCreatorFactory,它有一个名为 IRobot createRobot(String robotName) {...} 的方法(看到您的机器人返回一个名称。我的建议是每个机器人都有一个 public static String name NAME = Robocop.class.getName();。在该方法中,您将有一个检查比如

if (Robocop.NAME.equals(robotName) { return new RoboCop(); }

这样,你减轻了instanceof。此外,您可以在DeploymentVisitor 上使用@Meriton 的建议(使用访问者模式)......

PS 我的例子是对工厂方法模式的粗略解释。 GoF 书籍和维基百科中有一个例子。

【讨论】:

  • 谢谢,问题不在于对象的创建,而在于如何设计代码来“接收”对象,这些对象遵循一个接口,并正确处理实现.
  • 啊,深夜的 SO 让大脑变慢了。您可以拥有public void deploy(IRobot robot) {System.out.println("A " + robot.getName() + " has been deployed."); }。只有在运行时,您的 jvm 才会传递 IRobot 的具体类。我完全误解了你的问题。
猜你喜欢
  • 2011-06-30
  • 2011-10-18
  • 1970-01-01
  • 2019-11-21
  • 1970-01-01
  • 2017-02-16
  • 1970-01-01
  • 2012-05-24
  • 1970-01-01
相关资源
最近更新 更多