Implementation advice (instanceof and interaction between subclasses)
I always see people railing on using instanceof as an example of poor OOP. I completely understand the argument, but the examples used are typically ones involving fairly shallow polymorphism (Animal.go(), animal.talk(), etc.)
When it comes to cases of interaction between the various subclasses, can instanceof be considered more acceptable? To continue the animal analogy, let's look at
Animal_A.eat(Animal_B);
If we say that dogs eat cats, cats eat rats, and rats eat dogs (they're huge), is there a good implementation that doesn't say
if(Animal_B instanceof Cat) Animal_B.die();?
I could use some kind of enum Type{CAT, RAT, DOG}, but that seems to just be duplicating instanceof in more lines of code.
Any advice?
Re: Implementation advice (instanceof and interaction between subclasses)
Method overloading? Maybe something like Code:
//Animal, abstract class
public boolean eat(Animal other) {
die();
return false;
}
// Dog extends Animal
public boolean eat(Cat other) {
System.out.println("yum!"); // poor Cat
return true;
}
// Cat extends Animal
public boolean eat(Rat other) {
System.out.println("yum!"); // good riddance
return true;
}
// Rat extends Animal
public boolean eat(Dog other) {
System.out.println("yum!"); // BURP
return true;
}
db
Re: Implementation advice (instanceof and interaction between subclasses)
That looks like exactly what I was thinking of - I didn't think to check if Java could overload with more specific sub classes, I figured it would only overload eat(Animal a);. I presume then that in your example, Cat_C.eat(Dog_D); would trigger die(); and return false? Thank you very much!
Re: Implementation advice (instanceof and interaction between subclasses)
Db -
So I finished up the testing portions of my program. As far as I can tell, Java does not selectively choose between "better" and "worse" overloads. I appreciate the help though.
Re: Implementation advice (instanceof and interaction between subclasses)
Quote:
Originally Posted by
Diargg
Db -
So I finished up the testing portions of my program. As far as I can tell, Java does not selectively choose between "better" and "worse" overloads. I appreciate the help though.
What exactly do you mean by "better" and "worse" overloads? It's not any terminology I've heard before.
Read the rules of method overloading here: Chapter*8.*Classes
db
Re: Implementation advice (instanceof and interaction between subclasses)
Sorry, I wan't specific. What you suggested does not work - you cannot overload with different class types, even if they subclass. At least, Eclipse doesn't do it in JRE1.7
Re: Implementation advice (instanceof and interaction between subclasses)
Quote:
you cannot overload with different class types, even if they subclass.
Rubbish. You're doing something else wrong. Copy this and save it as Animal.java, then compile and execute it. Code:
public abstract class Animal {
public boolean eat(Animal other) {
die();
return false;
}
private void die() {
System.out.println("RIP");
}
protected void yum() {
System.out.println("YUM");
}
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
Rat rat = new Rat();
System.out.print("Dog eat Cat: ");
dog.eat(cat);
System.out.print("Dog eat Rat: ");
dog.eat(rat);
System.out.print("Cat eat Rat: ");
cat.eat(rat);
System.out.print("Cat eat Dog: ");
cat.eat(dog);
System.out.print("Rat eat Dog: ");
rat.eat(dog);
System.out.print("Rat eat Cat: ");
rat.eat(cat);
}
}
class Dog extends Animal {
public boolean eat(Cat other) {
yum();
return true;
}
}
class Cat extends Animal {
public boolean eat(Rat other) {
yum();
return true;
}
}
class Rat extends Animal {
public boolean eat(Dog other) {
yum();
return true;
}
}
db
Re: Implementation advice (instanceof and interaction between subclasses)
Yes, that works because the compiler 'knows' that a dog is a Dog and a cat is a Cat etc. If that dog, cat etc. would've been of class Animal the compiler would generate a call to the superclass method instead for all cases. Method overloading is compiler thing.
kind regards,
Jos
Re: Implementation advice (instanceof and interaction between subclasses)
Quote:
Originally Posted by
JosAH
Yes, that works because the compiler 'knows' that a dog is a Dog and a cat is a Cat etc. If that dog, cat etc. would've been of class Animal the compiler would generate a call to the superclass method instead for all cases. Method overloading is compiler thing.
kind regards,
Jos
Which would explain why
Code:
Dog dog = new Dog();
Cat cat = new Cat();
Rat rat = new Rat();
Animal[] ark = {new Dog(), new Cat(), new Rat()};
ark[0].eat(ark[1]);
ark[0].eat(cat);
results in RIP RIP. Makes sense I suppose. So in the above example, would you suggest using instanceof?
Re: Implementation advice (instanceof and interaction between subclasses)
Quote:
Originally Posted by
Diargg
Which would explain why
Code:
Dog dog = new Dog();
Cat cat = new Cat();
Rat rat = new Rat();
Animal[] ark = {new Dog(), new Cat(), new Rat()};
ark[0].eat(ark[1]);
ark[0].eat(cat);
results in RIP RIP. Makes sense I suppose. So in the above example, would you suggest using instanceof?
Probably; but I'd do it in the Dog, Cat, Rat classes themselves; e.g. the eat(Animal victim) method in the Dog class could look like this:
Code:
public boolean eat(Animal victim) {
boolean edible= victim instanceof Cat;
if (edible)
yum();
else
die();
return edible;
}
the inherited methods in the other classes are similar.
kind regards,
Jos
Re: Implementation advice (instanceof and interaction between subclasses)
Ok, that's how I've been doing it. Guess that's something of an affirmation! You've been a great help, thanks.
Re: Implementation advice (instanceof and interaction between subclasses)
Code:
public abstract class Animal {
protected Set<Class> edible = new HashSet<Class>();
public boolean eat(Animal other) {
if (edible.contains(other.getClass())) {
yum();
return true;
}
die();
return false;
}
private void die() {
System.out.println("RIP");
}
protected void yum() {
System.out.println("YUM");
}
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
Rat rat = new Rat();
System.out.print("Dog eat Cat: ");
dog.eat(cat);
System.out.print("Dog eat Rat: ");
dog.eat(rat);
System.out.print("Cat eat Rat: ");
cat.eat(rat);
System.out.print("Cat eat Dog: ");
cat.eat(dog);
System.out.print("Rat eat Dog: ");
rat.eat(dog);
System.out.print("Rat eat Cat: ");
rat.eat(cat);
}
}
class Dog extends Animal {
public Dog() {
this.edible.add(Cat.class);
}
}
class Cat extends Animal {
public Cat() {
this.edible.add(Rat.class);
}
}
class Rat extends Animal {
public Rat() {
this.edible.add(Dog.class);
}
}
:)
Re: Implementation advice (instanceof and interaction between subclasses)
Very nice. Did not even think of that one. Thanks!
Re: Implementation advice (instanceof and interaction between subclasses)
Cool; instead of doing an instanceof test we do a set presence test ... plus an entire set per class. ;-)
kind regards,
Jos
Re: Implementation advice (instanceof and interaction between subclasses)
Quote:
Originally Posted by
JosAH
Cool; instead of doing an instanceof test we do a set presence test ... plus an entire set per class. ;-)
kind regards,
Jos
Indeed - from a performance standpoint it does not strike me as the most efficient solution. However, it could have merits - let's say there was a Animal.Hunt() method that searched out it's prey? The set could certainly be used elsewhere.
Re: Implementation advice (instanceof and interaction between subclasses)
Quote:
Originally Posted by
JosAH
Cool; instead of doing an instanceof test we do a set presence test ... plus an entire set per class. ;-)
kind regards,
Jos
Actually, it's an entire Set per instance of a class...:)
Never do things by halves, that's what I say.
It's not actually intended as a serious answer, but more to highlight the inheritance here.
As in, the inheritance is an illusion largely due to the simplicity of the problem, hence the problem surrounding how to get animal A to eat animal B without doing instanceof or class comparisons. And the answer is to provide data (attributes) to Animal that allow it to identify predator/prey. Not to subclass Animal.
Re: Implementation advice (instanceof and interaction between subclasses)
Hit Reply too soon!
Then end result would be something akin to an AnimalSpecies.
Individual Animals would have an AnimalSpecies.
The Species defines what other Species it can eat or be eaten by.
Composition.
Re: Implementation advice (instanceof and interaction between subclasses)
So Tolls, you would suggest having
Code:
Animal dog = new Animal(new AnimalSpecies(AnimalSpecies.DOG));
//DOG is enum in Species, Animal declaration takes a type of species and works with it
I'm not entirely sure though where down the rabbit's hole you escape doing set- or instanceof-based class comparisons
Re: Implementation advice (instanceof and interaction between subclasses)
Not Enums (unless you really know this is a fixed set of Species).
You'd have 3 instances (in your case) of an AnimalSpecies class.
Each instance would hold references to the species it could eat.
You would then have any number of instances of Animal class, each of which would hold a reference to one of the AnimalSpecies objects.
Figuring who could eat who then would be easy:
Code:
public boolean eat(Animal other) {
if (this.animalSpecies.canEat(other.getAnimalSpecies())) {
yum();
return true;
} else {
die();
return false;
}
}
canEat(AnimalSpecies) simply does a check on the Set<AnimalSpecies> of things that species can eat, similar to the one I have above involving Class.
That shoudl strip out the need to know Classes, and makes (in my mind) a neater model.
Re: Implementation advice (instanceof and interaction between subclasses)
Tolls,
At what point does this simplicity break down though? What separates this from just being a fancier implementation of instanceof? Outside of some scalability benefits, I'm somewhat lost.