the cat can attack a dog or another cat which both happen to be animals, I am curious as to why the burden of casting the other animal to either dog or cat is on me, why doesn't java try and see if it is either cat or dog because I have two methods that match either a cat or a dog and then throw an error if it was an elephant instead?
Unfortunately, what you are suggesting is not aligned with current java compiler implementation's method matching system.
The compiler has to perform what is referred as static type checking, which consists basically in checking statically (i.e. before the program is run) whether the code is type-safe or not, which would in the latter case result in an error at run-time.
As an example, consider:
float b = 4.6f;
int a = 5 + b;
Here the compiler knows that b is of type float, knows that operator + has an entry for (int + float = float), and knows that a is of type integer. Thus, it infers that the expression has type float and must be cast to integer to be assigned to a. This conversion is not permitted in java, so it outputs an error to prevent loss of precision.
With objects, it is basically the same, knowing that classes deeper in hierarchy can be "converted" to their shallower parents, but not the contrary.
Therefore, in your case:
public void attack(Animal other) {
catAttack(other); <-------- Problem here
}
private void catAttack(Cat other) {
// Maybe a meow showdown wins the fight, no need to get physical
}
private void catAttack(Dog other) {
// Dogs are dangerous, run!
}
The java compiler can not infer the real class of variable other
in method attack(Animal other)
without running the whole program.
It can only know that the "pointer" is of type Animal, so the real class can be anything that extends animal, and it has to type-check (and statically resolve method invocation) your call to catAttack(other)
knowing only that.
Since there is no catAttack(Animal)
method, java compiler cannot guarantee type safety for your code, and outputs an error.
What you can do instead, is:
public static abstract class Animal {
public abstract void attack(Animal other);
public abstract void beAttackedByCat(Animal cat);
public abstract void beAttackedByDog(Animal dog);
}
public static class Cat extends Animal {
@Override
public void attack(Animal other) {
other.beAttackedByCat(this);
}
public void beAttackedByCat(Animal cat){ // cat > cat }
public void beAttackedByDog(Animal dog){ // dog > cat }
}
public static class Dog extends Animal {
@Override
public void attack(Animal other) {
other.beAttackedByDog(this);
}
public void beAttackedByCat(Animal cat){ // cat > dog }
public void beAttackedByDog(Animal dog){ // dog > dog }
}
Which is probably miles away from the perfect solution, but just to give you the idea of what you can do and what you can not.