2

I have a java programming related problem. I have the following classes:

public abstract class Animal {
    abstract void walk(AbstractWalkData abstractWalkData);
}

public class Mouse extends Animal{
    @Override
    void walk(AbstractWalkData abstractWalkData) {
        System.out.println(abstractWalkData.getWalkSound());
        System.out.println(abstractWalkData.getWalkSpeed());
        System.out.println(((MouseWalkData) abstractWalkData).getMouseSpecific());

    }
}

public class Tiger extends Animal{
    @Override
    void walk(AbstractWalkData abstractWalkData) {
        System.out.println(abstractWalkData.getWalkSound());
        System.out.println(abstractWalkData.getWalkSpeed());
        System.out.println(((TigerWalkData) abstractWalkData).getTigerSpecific());
    }
}

@Data
@SuperBuilder
public abstract class AbstractWalkData {
    private String walkSound;
    private int walkSpeed;
}

@Getter
@Setter
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
public class MouseWalkData extends AbstractWalkData {
    private String mouseSpecific;
}

@Getter
@Setter
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@SuperBuilder
public class TigerWalkData extends AbstractWalkData {
    private String tigerSpecific;
}

public class MainClass {
    public static void main(String[] args) {
        Animal mouse = new Mouse();
        MouseWalkData mouseData = MouseWalkData.builder()
                .walkSound("mouse walk sound")
                .walkSpeed(5)
                .mouseSpecific("mouse specific string")
                .build();
        mouse.walk(mouseData);
        Animal tiger = new Tiger();
        TigerWalkData tigerWalkData = TigerWalkData.builder()
                .walkSound("tiger walk sound")
                .walkSpeed(8)
                .tigerSpecific("tiger specific string")
                .build();
        tiger.walk(tigerWalkData);
    }
}

Note that I am using lombok to get rid of boilerplate code. Is there any way to get rid of the castings in the walk method of the Mouse and Tiger class? I would like to do something like this:

public class Mouse extends Animal{
    @Override
    void walk(MouseWalkData mouseWalkData) {
        System.out.println(mouseWalkData.getWalkSound());
        System.out.println(mouseWalkData.getWalkSpeed());
        System.out.println(mouseWalkData.getMouseSpecific());

    }
}

But i get a compiler error. Thank you all for any ideas.

Tugrul Bayrak
  • 111
  • 1
  • 15
  • You seem to be violating the [LSP](https://en.wikipedia.org/wiki/Liskov_substitution_principle), see [here](https://stackoverflow.com/questions/56860/what-is-an-example-of-the-liskov-substitution-principle) for more info. – Sweeper Dec 23 '19 at 13:22
  • @Sweeper I do not think so. All my child class instances can replace the parent class instance. Can you explain where you think i broke LSP? – GeordiLaForge Dec 23 '19 at 14:01

2 Answers2

2

That's how Java works but what you can do is start using generics in your code, for example, it will be something like this

abstract class Animal <T extends AbstractWalkData > {
    abstract void walk(T walkData);
}

and then, for example, your mouse class will be something like this

public class Mouse extends Animal <MouseWalkData>{
    @Override
    void walk(MouseWalkData mouseWalkData) {
        System.out.println(mouseWalkData.getWalkSound());
        System.out.println(mouseWalkData.getWalkSpeed());
        System.out.println(mouseWalkData.getMouseSpecific());

    }
}

and it will work

to learn more about generics I recommend to read about them and how it helped to avoid some of java's boilerplate code https://www.baeldung.com/java-generics

Karim
  • 1,004
  • 8
  • 18
  • 1
    @GeordiLaForge Note that nothing actually prevents you from making a `class Mouse extends Animal`. – Sweeper Dec 23 '19 at 14:05
0

You can use 'instanceof' keyword to do that in Java. If you use concrete classes as parameter in your walk method as you mentioned, it is not polymorphism anymore. You should use an abstract class or interface in the parameter. Then pass any concrete child object to that method.

if(abstractWalkData instanceof MouseWalkData) {
   System.out.println((MouseWalkData)abstractWalkData.getMouseSpecific());
}
Tugrul Bayrak
  • 111
  • 1
  • 15
  • Thank you for your answer. Exactly if i use the concrete type as parameter I lose the polymorphism. Anyway what ist the benefit of instanceof vs casting? – GeordiLaForge Dec 23 '19 at 13:21
  • Instanceof an instance of the specified type (class or subclass ), usually before casting but usually you should avoid them because it makes your code smells, and use them if you're absolutely need them – Karim Dec 23 '19 at 13:25
  • My answer was completing your code. If you do not want to use instanceof and casting, you may use generics as mentioned above. But if you use, you should be sure about its concrete class then cast. – Tugrul Bayrak Dec 23 '19 at 13:38