简体   繁体   中英

Casting the right subclass at runtime

I have the following base and derived classes :

public abstract class Drone {
    public void Drone(){}
    public abstract boolean commonFunc();
}

public class DroneMain extends Drone {
    @Override
    public boolean commonFunc(){
        return false;
    }

    public boolean droneMainFunc(){
        return true;
    }
}

public class DroneOther extends Drone {
    @Override
    public boolean commonFunc(){
        return true;
    }

    public boolean droneOtherFunc(){
        return false;
    }
}

public class DroneMgr {
    public enum DroneType {MAIN, OTHER}

    public Drone getDrone(DroneType type){
        if (type.equals(DroneType.MAIN))
            return new DroneMain();
        else
            return new DroneOther();
    }
}

The code that calls these classes is as follows:

private DroneManager droneManager;
private Drone drone;

droneManager = new DroneManager();
drone = droneManager.getDrone(DroneManager.DroneType.MAIN);
boolean value = drone.droneMainFunc();

My Question is regarding the following lines:

drone = droneManager.getDrone(DroneManager.DroneType.MAIN);
boolean value = drone.droneMainFunc();

How do I make it so that I can call droneMainFunc() from the drone object?

Otherwise, how do I do that if I do not know its type until runtime. If I cannot use the Drone (drone instance) then I wouldn't know until runtime, I would have to create both a DroneMain object and a DroneOther object and then only assign/cast one based on the returned type. I would like to avoid that as I am sure there is a better pattern to this.

The most simple way would be the usage of the instanceof operator.

drone = droneManager.getDrone(DroneManager.DroneType.MAIN);
if (drone instanceof DroneMain) {
    ((DroneMain)drone).droneMainFunc();
} ...

But i´d consider including another abstract method into your Drone class which would simply call the correct function. That´s because it seems that you want to execute a function indepent on it´s actually class. If so you should include another common function in the abstract class, which both implentations could use aswell, as your Drone#commonFunc .

public abstract class Drone {
   public void Drone(){}
   public abstract boolean commonFunc();
   public abstract boolean doSomething();
}

public class DroneMain extends Drone {
   @Override
   public boolean commonFunc(){
       return false;
   }

   @Override
   public boolean doSomething() {
       return droneMainFunc();
   }

   public boolean droneMainFunc(){
       return true;
   }
}

public class DroneOther extends Drone {
   @Override
   public boolean commonFunc(){
       return true;
   }

   @Override
   public boolean doSomething() {
       return droneOtherFunc();
   }

   public boolean droneOtherFunc(){
       return false;
   }
}

...

boolean value = drone.doSomething();

I've got two options for you:

Skip the enums and use classes and generics instead. This allows you to make assertions in DroneMgr about type and return the correct one as the correct type.

public class DroneMgr {
    public <T extends Drone> T getDrone(Class<T> type) {
        if(DroneMain.class.equals(type)) {
            // Need to cast to T, but we already know that T is DroneMain
            return (T) new DroneMain();
        } else if (DroneOther.class.equals(type)) {
            return (T) new DroneOther();
        } else {
            throw new IllegalArgumentException("Unknown Drone class " + type);
        }
    }
}

To use:

DroneMain drone = droneManager.getDrone(DroneMain.class); // No need to cast
boolean value = drone.droneMainFunc();

Another option is to use the strategy pattern and implement the functionality directly in the enums, this makes it easy to add new types.

public enum DroneType {
    MAIN {
        @Override
        public boolean handle() {
            return new DroneMain().droneMainFunc();
        }
    },
    OTHER {
        @Override
        public boolean handle() {
            return new DroneMain().droneOtherFunc();
        }
    };

    public abstract boolean handle();
}

Then you just do:

boolean value = DroneType.MAIN.handle();

If you're not a fan of writing the implementation in the enums you can break out the functionality to separate classes that all implement a common interface and just have a reference to the instance in the enums.

public interface DroneHandler {
    boolean handle();
}

public class DroneMainHandler implements DroneHandler {
    @Override
    public boolean handle() {
        return new DroneMain().droneMainFunc();
    }
}

public class DroneOtherHandler implements DroneHandler {
    @Override
    public boolean handle() {
        return new DroneOther().droneMainFunc();
    }
}

public enum DroneType {
    MAIN(new DroneMainHandler()),
    OTHER(new DroneOtherHandler());

    public final DroneHandler handler;

    private DroneType(final DroneHandler handler) {
        this.handler = handler;
    }
}

Usage then becomes:

boolean value = DroneType.MAIN.handler.handle();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM