views:

151

answers:

5

I have read other related posts, but am still not quite sure how, or if it is possible to Dynamically cast (Interface to Implementation) in Java. I am under the impression that I must use Reflection to do so.

The particular project I am working on requires a usage of many instanceof checks, and it is, in my opinion getting a bit out of hand, so would appreciate any ideas/solutions.

Below is a mini example I wrote up just to clarify exactly what I'm wanting to do . Let me know if you need more information:

Interface:

public interface IRobot {
    String getName();
}

Implementations:

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; }
}

The Class that handles the implementations:

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
    }
}

Output:

[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?

So, to sum up my question, how can I completely avoid having to use instanceof in this case. Thanks.

+5  A: 

You can make deploy a method of IRobot, or use the visitor pattern.

And no, reflection will not make things any easier here.

meriton
Yes, that was my initial whim as well. I typically try to avoid Reflection if possible
KennyCason
Actually this is working quite nice in my demo. I will try it on the actual product code, which has quite a bit more degrees of complexity to it, and let you know how it goes. Thanks.
KennyCason
Excellent, you sir, just helped me delete 100's of lines of code!
KennyCason
And an additional note. My real-life code actually uses an `Abstract Class` So in that case do not fill in the body of the deploy() method in the Abstract class, but instead make it an abstract method and define it in each class that calls deploy (i.e. treat it like an interface)
KennyCason
+2  A: 

You can avoid instanceof by moving the deploy method in your IRobot interface and implementations.

The explanation of the behavior is that your three deploy methods are three different methods; overloaded methods with different signatures. At compile time, it's determined which one is chosen, not at runtime based on the real class...

ivy
"At compile time, it's determined which one is chosen, not at runtime based on the real class" - thanks, I was not aware of that.
KennyCason
+1  A: 

Kent Beck says in his book Test Driven Development: Any time you're using run-time type-checking, polymorphism should help. Put the deploy() method in your interface and call it. You'll be able to treat all of your robots transparently.

Forget Reflection, you're just over thinking it. Remember your basic Object Oriented principles.

Mike
Right, I figured I was making it too complicated. Again, the idea of using reflection was a suggestion made to me.. though, i'm not a huge fan of it when it's not needed. :P
KennyCason
It's okay. It's good to learn the right way.
Mike
What really makes it tough is trying to modify existing code. Especially when it's code that has been worked on by many people for many years.. it becomes tougher to spot "the best thing to do" at the time
KennyCason
Absolutely. The best thing you can do then is to reverse engineer some diagrams, write some tests, and use automated refactoring tools (If you're using Java). It's usually pretty unpleasant, though.
Mike
+3  A: 

Dispatch of overloaded methods is done statically at compiletime, so your approach cannot be made to work. It's also very bad design. Doesn't it strike you as peculiar that the getName() method, the only thing that differs between the robot classes, is never actually called?

You have to ditch the overloaded methods, and instead use method overriding of methods in the robot classes, which you call directly. i.e.

public void deploy(IRobot robot) {
    System.out.println("A "+robot.getName()+" has been received..."
                        +" preparing for deployment.");
    // preparing for deployment
}
Michael Borgwardt
thanks, everything made sense and the demo worked for me. Though I want to be sure that I fully understand what you meant by "method overriding of methods", you would say that thedeploy method is defined in the RobotFactory correct? and what changes should be made to the IRobot Impelementations? (other thant the getName method, I just slapped it in there for the demo)
KennyCason
@KennyCason: method overriding isn't the best word, since you're using an interface. What you want is dynamic dispatch. You get that when you have different classes that implement the same method (same name, same parameters) differently. When you call such a method, the implementation of the actual runtime class of the object on which you call it is used. If you use overriding (same name, different parameters), that does not happen, instead the compile time type of the reference determines which implementation is used.
Michael Borgwardt
ah. got it. That is an important distinction
KennyCason
"overriding (same name, different parameters)" - I assume you mean over_loading_?
meriton
@meriton: argh, yyou're right of course
Michael Borgwardt
A: 

Instead of using instanceof you can use the Factory Method Pattern

Definition of Factory method...

Like other creational patterns, it deals with the problem of creating objects (products) without specifying the exact class of object that will be created.

You will need a RobotCreatorFactory that will have a method called IRobot createRobot(String robotName) {...} (seeing that your robot returns a name. My suggestions is that each robot will have a public static String name NAME = Robocop.class.getName();. Inside the method you'll have a check such as

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

That way, you alleviate instanceof. And also, you can use @Meriton's advice on a DeploymentVisitor (using a visitor pattern)....

PS My example is a rough explanation of the Factory method pattern. An example exists in GoF book and Wikipedia.

The Elite Gentleman
Thanks, The problem isn't so much on the creation of the objects as it is how to design the code to "receive" the objects, of which adhere to an Interface, and properly handle the implementations.
KennyCason
Ah, late night on SO makes the brain slow. You can have `public void deploy(IRobot robot) {System.out.println("A " + robot.getName() + " has been deployed."); }`. Only on runtime will your jvm pass the concrete classes of IRobot.I totally misinterpreted your question.
The Elite Gentleman
no problem. It happens all the time :)
KennyCason