0

i have a Device parent class like this

class Device{
    int id;
    public Device(int id);
}

and some child devices

class SwitchDevice extends Device{
    boolean state;
    public SwitchDevice(int id);
    boolean getState();
    void setState(boolean state);
}

class LightDevice extends SwitchDevice{
    int value;
    public SwitchDevice(int id);
    int getValue();
    void setValue(int value);
 }

and then i have a Device handler which has a list of Device objects and some methods to retreive device instances from the list

class DeviceHandler {

    private List<Device> deviceList;

public DeviceHandler() {
        deviceList = new ArrayList<Device>();
}

public Device getById(int devId);
}

i want to know how can i call the childs methods from this list what i mean is something like

Device dev = deviceHandler.getById(0);
boolean state = dev.getState;

i know that in java this is not possible, but maybe you can suggest me how to achieve de result.

i have tried the visitor pattern but in my case is not the right one because it doesn't allow me to return the value.

the only way seems to be adding in the handler class a method for each value of each device like this

boolean getSwitchState(Device dev){
    if(dev.instanceOf(SwitchDevice)){
     SwitchDevice device = (SwitchDevice)dev;
     return device.getState();
    }else{
      throw new Exception();
}

but it needs a lot of code and is not safe.

I hope you understand what i mean (i'm not so good in english, and not an expert java programmer).

Chris Martin
  • 30,334
  • 10
  • 78
  • 137
  • 1
    "but it needs a lot of code and is not safe." - firstly: that is safe. secondly: writing lots of code is the price you pay if your do weird things... Why would you need such a method, what are you trying to achieve and why? – luk2302 Feb 22 '18 at 16:34
  • 1
    How is safely casting not safe? How is writing lots of code a bad thing? We could have entire programs with class names being a single letter to save on file size, but we're already writing in Java as is, so code size as a consideration is minimal at best except for the most optimized of software. – Compass Feb 22 '18 at 16:36
  • 1
    One possibility is a Double Dispatch pattern, but it's even more code and has large disadvantages (e.g. tightly coupling parent to children) –  Feb 22 '18 at 16:39
  • i don't know, i have read lots of pages saying that using istanceOf method is not a good practice, as i said i am not an expert in java i am self studying it in the freetime by a couple of months – user8770209 Feb 22 '18 at 16:42
  • What pages say instanceOf is not good? It is needed in a lot of situations. – LHA Feb 22 '18 at 16:44
  • @luk2302 i am trying to create a java server to control some home devices built on esp8266 and using a device list is the only way i know to have the states updated for every device but i also need to retrieve and set the values for each device, obviusly the server is more than this: it has sockets for comunication and a list of clients (android app) which control the devices – user8770209 Feb 22 '18 at 16:47

2 Answers2

1

1. Use instanceof

You already use instanceof but I don't understand why need a lot of code here. It is safe.

    Device dev = deviceHandler.getById(0);
    if (dev instanceof SwitchDevice) {

        ((SwitchDevice)dev).getState()
    }
    // More check instanceof

2. Use Reflection

    try {
        // Get public getState()
        Method m = dev.getClass().getMethod("getState");
        Boolean state = (Boolean )m.invoke(dev);

    } catch (NoSuchMethodException ex) {
       // dev is not SwitchDevice
    }

3. Add all common behaviors into Device base class (OR Interface?)

class Device{
    // ...
    public boolean getState() {
        throw new UnsupportedOperationException();
    }

    public int getValue() {
        throw new UnsupportedOperationException();
    }
}

class SwitchDevice extends Device {
    // ...

    @Override
    public boolean getState() {
        return this.state;
    }
}

class LightDevice extends SwitchDevice {
    // ...
    @Override
    public int getValue() {
        return this.value;
    }
}

For this solution. You need to be aware of UnsupportedOperationException

LHA
  • 9,398
  • 8
  • 46
  • 85
  • now it's clearer I was reading and learning so many things and patterns that i was getting a little confused as you can see. thank you all for clearing my ideas – user8770209 Feb 22 '18 at 16:57
0

If it is unavoidable to deal with casting at least do it in one place. Since the calling code already expects a particular subclass from getById method then update that method to be generic and do all the casting inside it:

    public <T extends Device> Optional<T> getById(Class<T> deviceType, int devId){
        Device d = deviceList.get(devId);
        if ( d == null || !deviceType.isInstance(d) ) return Optional.empty();                        
        return Optional.of( deviceType.cast(d) );
    }

And then call it like this:

    Optional<SwitchDevice> sd = deviceHandler.getById(SwitchDevice.class, 1);        
    boolean state = sd.orElseThrow( () -> new Exception() ).getState();

or one liner:

    boolean state = deviceHandler.getById(SwitchDevice.class, 1)
                                 .orElseThrow( () -> new Exception() )
                                 .getState();
tsolakp
  • 5,858
  • 1
  • 22
  • 28