Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
Suppose we had this class
Animal.java
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public void shout() {
/* must be override */
}
public String getName() {
return name;
}
}
Driver.java
import java.util.ArrayList;
import java.util.List;
public class Driver {
public static void main(String[] args) {
List<Animal> animals = new ArrayList<Animal>();
animals.add(new Animal("Dog") {
#Override
public void shout() {
System.out.println(getName() + " sounds like: woof woof woof");
}
});
animals.add(new Animal("Cat") {
#Override
public void shout() {
System.out.println(getName() + " sounds like: meow meow meow-ow");
}
});
for(Animal a : animals)
a.shout();
}
}
how can i boost me to implement the method shout() or any other programmer? Suppose that there is no reason to make the Animal class abstract, and make a class for Dog,Cat, etc.. that extends Animal
Is there a design pattern?
mmm a Strategy pattern could help you.
public class Animal {
private Shoutable shoutable;
private String name;
public Animal(String name) {
this.name = name;
}
public void shout() {
shoutable.shout();
}
public String getName() {
return name;
}
public void setShout(Shoutable shoutable){
this.shoutable=shoutable;
}
}
public interface Shoutable{
void shout();
}
public class DogShout implements Shoutable{
#Override
public void shout{
System.out.println("woff gua guaa gua woff");
}
}
You could implement shout() in the base class and add method voice() that returns the voice of the animal.
Like this:
public class Animal {
private String voice;
private String name;
public Animal(String name, String voide) {
this.name = name;
this.voice = voice;
}
public void shout() {
System.out.println(getName() + " sounds like: "+ getVoice());
}
public String getName() {
return name;
}
public String getVoice() { return voice }
}
In the code that creates animals:
animals.add(new Animal("Dog", "woof"));
animals.add(new Animal("Cat", "meow");
If you want Cat and Dog to do the things that Animal does without animal being Abstract, you can.
public class Cat extends Animal
that will work if Animal is Abstract or a normal, fully implemented class. You would use implements instead of extends for an interface.
You can do the following:
public class Animal {
private String name;
private Shoutable shoutable;
public Animal(String name, Shoutable shoutable) {
this.name = name;
this.shoutable = shoutable;
}
public void shout() {
shoutable.shout();
}
public String getName() {
return name;
}
}
The Shoutable interface:
public interface Shoutable {
void shout();
}
And have a shoutable implementation as anonymous inner class:
import java.util.ArrayList;
import java.util.List;
public class Driver {
public static void main(String[] args) {
List<Animal> animals = new ArrayList<Animal>();
animals.add(new Animal("Dog", new Shoutable() {
#Override
public void shout() {
System.out.println(getName() + " sounds like: woof woof woof");
}
});
animals.add(new Animal("Cat", new Shoutable() {
#Override
public void shout() {
System.out.println(getName() + " sounds like: meow meow meow-ow");
}
});
for(Animal a : animals)
a.shout();
}
}
Related
So the compiler complains when ever i do a explicit cast. I can prevent this by using a #SuppressWarnings annotation.
At this point i would have this annotation a lot in my code which lets me suspect that there is another way i'm just not aware of.
Lets have a look at this example
class CutePet
{
public void pet()
{
System.out.println( "The cute pet gets some pets" );
}
}
class Cat extends CutePet
{
public void letOutside()
{
System.out.println( "The cat goes outside" );
}
public void letInside()
{
System.out.println( "The cat comes inside" );
}
public void removeTick()
{
System.out.println( "The cat looses all ticks" );
}
}
class Dog extends CutePet
{
public void goForAWalk()
{
System.out.println( "The Dog goes for a walk" );
}
public void tellHimWhatHeIs()
{
System.out.println( "The Dog is a good boy" );
}
}
class caretaker
{
public void takeCare( CutePet pet )
{
if( pet instanceof Cat )
{
pet.pet();
((Cat)pet).letOutside();
((Cat)pet).letInside();
((Cat)pet).removeTick();
}
else if( pet instanceof Dog )
{
pet.pet();
((Dog)pet).goForAWalk();
((Dog)pet).tellHimWhatHeIs();
}
}
}
The Caretaker does not know what kind of Pet he will get in advance and he my has several pets of different kinds.
I tried to give the Cute pet class a getType() method which returns a enum. With this enum i can remove the "instanceof" but the cast is still there.
Am i missing something?
If this were a real world problem, the caretaker would recognize which kind of pet he has based on the pet's appearance. While "instance of" is one way of looking at it, you might want to consider overloading the takeCare method directly with the subtypes as required. For example:
class Caretaker {
public void takeCare(Cat pet) {
pet.pet();
pet.letOutside();
pet.letInside();
pet.removeTick();
}
public void takeCare(Dog pet) {
pet.pet();
pet.goForAWalk();
pet.tellHimWhatHeIs();
}
}
in other words, the caretaker knows what to do (has methods already in place) for the kind of pet he receives.
EDIT
In response to some of the comments, yes, the original example shifts the problem further up. If you have an array or a list of generic pets then you still have to figure out what kinds of pets you have to give them to the caretaker. Conceptually it seems strange that the pet should be able to pet itself, take itself for a walk, etc. (these methods are part of the pet class when it should be the caretaker doing these actions ON the pet).
I've since rewritten the code with a full working example below with a Job class that has a perform method. This method will return the appropriate job based on the type of animal the caretaker has. The caretaker can then perform the job on the pet in question. See below.
Doing things this way avoids instanceof. While it is debatable how good/bad instanceof actually is, where possible it should be the object itself to tell me what it needs, otherwise the whole polymorphism concept can get pretty hairy pretty quick.
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Arrays.asList(
new Cat("Cat1"),
new Cat("Cat2"),
new Dog("Dog1")
).forEach(caretaker::takeCare);
}
interface CutePet {
String whoAmI();
Job whatINeed();
}
abstract static class NamedCutePet implements CutePet {
private final String name;
public NamedCutePet(String name) {
this.name = name;
}
public String whoAmI() {
return this.name;
}
}
static class Cat extends NamedCutePet {
public Cat(String name) {
super(name);
}
#Override
public Job whatINeed() {
return new CatJob(this);
}
}
static class Dog extends NamedCutePet {
public Dog(String name) {
super(name);
}
#Override
public Job whatINeed() {
return new DogJob(this);
}
}
static class Caretaker {
void takeCare(CutePet pet) {
pet.whatINeed().perform();
}
}
static abstract class BaseJob implements Job {
void pet(CutePet pet) {
System.out.println(String.format("The cute pet %s gets some pets", pet.whoAmI()));
}
}
static class DogJob extends BaseJob {
private final Dog dog;
public DogJob(Dog dog) {
this.dog = dog;
}
#Override
public void perform() {
pet(dog);
takeDogFarAWalk(dog);
tellHimWhatHeIs(dog);
}
private void takeDogFarAWalk(Dog dog) {
System.out.println(String.format("The dog %s goes for a walk", dog.whoAmI()));
}
private void tellHimWhatHeIs(Dog dog) {
System.out.println(String.format("The dog %s is a good boy", dog.whoAmI()));
}
}
static class CatJob extends BaseJob {
private final Cat cat;
public CatJob(Cat cat) {
this.cat = cat;
}
#Override
public void perform() {
pet(cat);
letOutside(cat);
letInside(cat);
removeTick(cat);
}
private void letOutside(Cat cat) {
System.out.println(String.format("The cat %s goes outside", cat.whoAmI()));
}
private void letInside(Cat cat) {
System.out.println(String.format("The cat %s comes inside", cat.whoAmI()));
}
private void removeTick(Cat cat) {
System.out.println(String.format("The cat %s loses all ticks", cat.whoAmI()));
}
}
interface Job {
void perform();
}
}
Let's make it clear: you can't call subclass specific methods without typecasting to subclass type.
Now, let me suggest an alternate way. Define a method takeCare() in the superclass and let the subclasses implement it by calling several specific methods specific to subclasses. Then from CareTaker#takeCare(), call only takeCare() method without typecasting.
Several other alternate approaches can be used to solve the situation.
Here is how you would do it with interfaces and reflection. Note that only the interface methods are called for each pet type. It could also be extended to call other methods.
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class PetProblem {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Dog dog = new Dog();
caretaker.takeCare(dog);
System.out.println("\nNow do it for the cat\n");
Cat cat = new Cat();
caretaker.takeCare(cat);
}
}
interface CuteCat {
void letOutside();
void letInside();
void removeTick();
}
interface CuteDog {
void goForAWalk();
void tellHimWhatHeIs();
}
interface CutePet {
default void pet() {
System.out.println("The cute pet gets some pets");
}
}
class Cat implements CutePet, CuteCat {
public void letOutside() {
System.out.println("The cat goes outside");
}
public void letInside() {
System.out.println("The cat comes inside");
}
public void removeTick() {
System.out.println("The cat looses all ticks");
}
}
class Dog implements CutePet, CuteDog {
public void goForAWalk() {
System.out.println("The Dog goes for a walk");
}
public void tellHimWhatHeIs() {
System.out.println("The Dog is a good boy");
}
}
class Caretaker {
public void takeCare(Object pet) {
Class<?>[] ifss = pet.getClass().getInterfaces();
for (Class<?> ifs : ifss) {
Method[] methods = ifs.getDeclaredMethods();
for (Method m : methods) {
try {
m.invoke(pet);
}
catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
Note however, that using interfaces and having a method so named that it can be used for all pets is easier. Here is an example. Since both dogs and cats need to eat, a common method feedMe() can be implemented for each.
public class AnimalShelter {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Dog dog = new Dog();
Cat cat = new Cat();
caretaker.feedThePets(dog);
caretaker.feedThePets(cat);
}
}
interface SupperTime {
void feedMe();
}
class Caretaker {
public void feedThePets(SupperTime pet) {
pet.feedMe();
}
}
class Dog implements SupperTime {
public void feedMe() {
System.out.println("Oh boy, Kibbles n' Bits");
}
}
class Cat implements SupperTime {
public void feedMe() {
System.out.println("Yum. Purina Cat Chow");
}
}
Im practicing polymorphism and inheritance, and i made a class (Animals) that sets the name of the animal, then i made a subclass (Cat) that sets the sound it makes, favourite toy.. all that. i tried testing it in a seperate class (Test) to print out "Cat likes to Moew, its favourite toy is Yarn" but its not working unless i extend Cat in the test class.
Heres my code.
Animals.java
public class Animals {
protected static String name;
public Animals() {
}
public Animals(String name) {
this.name = name;
}
public String setName(String newName) {
return this.name = newName;
}
public String getName() {
return name = name;
}
public static void animMove() {
System.out.println(name + " likes to walk");
}
}
Cat.java
public class Cat extends Animals {
public static String sound;
public static String favToy;
public String getSound(String sound) {
return this.sound = sound;
}
public String getToy(String favToy) {
return this.favToy = favToy;
}
public Cat() {
}
public Cat(String name, String sound, String favToy) {
super(name);
this.sound = sound;
this.favToy = favToy;
}
}
test.java
public class test{
public static void main(String[] args) {
Animals anim = new Animals();
Cat cat = new Cat("Cat", "moew", "Yarn ball");
System.out.println(anim.getName() + " Likes to " + cat.getSound(sound)
+ ", its favourite toy is a " + cat.getToy(favToy));
}
}
All works fine if i extend Cat to the test class, but when i dont, none of the variables like sound and favToy work. how would i do this without extending anything to the test class
Do not make the name variable static. This would mean that it belongs to the class and not an Animal object, meaning there will only ever be one Animal.name in the class. Your Cat.sound, Cat.favoriteToy variables are also static, which will mean all cats will have the same sound and same favorite toy (I guess this is acceptable, but then dont assign this in a constructor).
Setters don't need to have a return value (you are only changing some variable). For example:
public void setName(String newName) {
this.name = newName;
}
Getters do not need any parameters. You already know what to return, no need for a parameter. For example:
public String getSound() {
return this.sound;
}
Also, your Animals should be Animal, as this class represents a single animal.
If you create a Cat object, this will automatically be Animal as well (its inherited), so no need to create both, as you do in your main method
Cat myCat = new Cat("Purr","meow","ball"); //create cat
System.out.println(myCat.getName());
variables are static so all cats will have this name, sound and fav toy now...
Maybe you tried to do something like
1)
public class Animal {
protected String name;
public String animMove() {
return new String(this.name + " likes to walk");
}
}
2)
public class Cat extends Animal {
public String sound;
public String favToy;
public Cat(String name, String sound, String favToy) {
super(name);
this.sound = sound;
this.favToy = favToy;
}
public String getName() {
return super.name;
}
public String getSound() {
return this.sound;
}
public String getToy() {
return this.favToy;
}
}
3)
public class test{
public static void main(String[] args) {
Animal anim = new Cat("Cat", "moew", "Yarn ball");
System.out.println(anim.getName() + " Likes to " + anim.getSound(sound) + ", its favourite toy is a " + anim.getToy(favToy) + " " + anim.animMove());
}
}
I'm doing a simple Zoo application to understand oriented object concepts in Java.
My model is as follow:
1) A Zoo has a number of Cages
2) A Cage has a a mixture of Feline, Primate or Bird
3) An Animal can eat, sleep or drink
4) Feline extends Animal (Do extra Feline stuff)
5) Primate extends Animal (Do extra Primate stuff)
6) Bird extends Animal (Do extra Bird stuff)
The problem:
While it's pretty easy to handle x number of cages in a zoo (ArrayList of Cage), I'm struggling with the Animals in Cage.
I found out that I need to have an ArrayList of Object.
So far so good, but when I try to obtain my animal back and have him scratch a post, it's not a Feline anymore, it's an Object.
public class Cage{
private String name;
private ArrayList<Object> animals = new ArrayList<Object>();
public Cage(String name){
this.name = name;
}
public void addFeline(String name){
Feline newFeline= new Feline(name);
this.animals.add(newFeline);
}
public void addPrimate(String name){
Primate newPrimate= new Primate(name);
this.animals.add(newPrimate);
}
public void addBird(String name){
Bird newBird= new Bird(name);
this.animals.add(newBird);
}
public void removeAnimal(int index){
this.animals.remove(index);
}
public Object getAnimal(int index){
Object myAnimal = this.animals.get(index);
return myAnimal;
}
}
And the way I call it:
Zoo myZoo = new Zoo("My Zoo");
myZoo.addCage("Monkey Exhibit");
Cage myCage = myZoo.getCage(0);
myCage.addFeline("Leo");
Object MyAnimal = myCage.getAnimal(0);
The Question: How can I turn Object back into instance of class Feline so it can Scratch a Post?
I think the best way to approach this problem would be using the Strategy design pattern.
Feline, Primate and Bird should implement an interface Animal. The Cage would then have a method public void addAnimal(Animal animal);
The object creation for Feline, Primate and Bird should be outside of Cage.
I have put together some code if this can help. I would design the application similar to something below.
Behaviours should be encapsulated using interfaces. e.g. EatingBehaviour
public interface Animal {
public String getName();
}
public interface EatingBehaviour {
public void howManyTimes();
}
public class RealLionEatingBehaviour implements EatingBehaviour{
#Override
public void howManyTimes() {
System.out.println("I eat once a day");
}
}
public class ToyLionEatingBehaviour implements EatingBehaviour {
#Override
public void howManyTimes() {
System.out.println("I never eat! I am a toy lion.");
}
}
public abstract class Feline implements Animal{
public abstract void scratchPost();
private EatingBehaviour eatingBehaviour;
public EatingBehaviour getEatingBehaviour() {
return eatingBehaviour;
}
public void setEatingBehaviour(EatingBehaviour eatingBehaviour) {
this.eatingBehaviour = eatingBehaviour;
}
}
public class Lion extends Feline {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
Lion (String name) {
this.name=name;
}
public void scratchPost(){
System.out.println(getName() + " Lion Scratching Post!");
}
}
public class AnimalFactory {
public static Animal getAnimalInstance(String type, String name){
Animal animal=null;
if ("lion".equalsIgnoreCase(type)) {
animal = new Lion(name);
}
return animal;
}
}
import java.util.ArrayList;
import java.util.List;
public class Cage {
private List<Animal> animals = new ArrayList<Animal>();
public void addAnimal(Animal animal) {
animals.add(animal);
}
public void removeAnimal(int index){
this.animals.remove(index);
}
public Animal getAnimal(int index){
return this.animals.get(index);
}
}
public class Zoo {
public static void main(String args[]) {
Cage cage = new Cage();
Animal animal = null;
animal = AnimalFactory.getAnimalInstance("Lion", "Sweety");
cage.addAnimal(animal);
Animal animalFromCage = cage.getAnimal(0);
if (animalFromCage instanceof Feline) {
Feline feline = (Feline) animalFromCage;
feline.setEatingBehaviour(new RealLionEatingBehaviour());
feline.scratchPost();
feline.getEatingBehaviour().howManyTimes();
feline.setEatingBehaviour(new ToyLionEatingBehaviour());
feline.getEatingBehaviour().howManyTimes();
}
}
}
Use a cast:
Object myAnimal = myCage.getAnimal(0);
Feline f = (Feline) myAnimal;
private List<Animal> animals = new ArrayList<Animal>();
public void findAnimal(int index) {
Animal myAnimal = animals.get(index);
if (myAnimal instanceof Feline) {
Feline feline = (Feline) myAnimal;
//do the work with Feline
} else if (myAnimal instanceof Primate) {
//do the work with Primate
}
// continue with the other types.
}
this will avoid a unexpected classcast exception. Since you know the type use the super type (Animal) in the Arraylist instead fo Object. if it is Obejct you can add anything to the List.
You should use an ArrayList<? extends Animal> instead of ArrayList<Object>. You would then cast a return value to the appropriate subclass of Animal.
I was asked to finish this test in one of my interview and they wanted me to create classes intereface following all the right practices. I couldn't clear the interview but I sure am interested how you all experts out there would handle this.
Create an OODesign for the following scenario
There is a zoo, its has some animals.
Following are the animals which live in the zoo
Lion
Tiger
Bear
Each animal has enerygy level
Every animal speaks, eats, play
by defalut when animal eat they gain +8 energy level
by default when animal play they loose -5 energy level
by defalut when animal speak they loose -1
By default each animal speak "Grrr..."
By default every animal when they play they say "I am loving it"
Only bear can dance but when bear dance he says "Look MA' I am dancing"
Following are the foods available for animals
Grain, Steak and salmon
When soundOff() method gets called on Zoo
every animal speak
When Tiger speaks it says "ROARR..."
When Lion speaks he says "Don't you dare ask me"
I would structure Zoo to contain a collection of the Animal base class to start with:
class Zoo {
private Collection<Animal> animals;
...
public void soundOff() {
Iterator<Animal> i = animals.iterator();
while( i.hasNext() )
i.next().speak();
}
then the Animal base class has the common methods/properties: speak, eat, play, energy level; also allow for the animal to reject food they don't like in the eat() method:
abstract class Animal {
private int energyLevel;
private String playMessage;
private String speakMessage;
...
public Animal(String speakMessage, String playMessage) {
this.speakMessage = speakMessage;
this.playMessage = playMessage;
}
public Animal() {
this( "Grrr...", "I am loving it" );
}
...
public abstract void eat( Food f ) throws IllegalArgumentException;
public void play() {
System.out.println( playMessage );
energyLevel += 8;
}
public void speak() {
System.out.println( speakMessage );
energyLevel -= 1;
}
..
}
then you can have specialized classes -- like DancerAnimal:
abstract class DancerAnimal extends Animal {
private String danceMessage;
public DancerAnimal(String speakMessage, String playMessage, String danceMessage) {
super( speakMessage, playMessage );
this.danceMessage = danceMessage;
}
public DancerAnimal( String danceMessage ) {
super();
this.danceMessage = danceMessage;
}
public void dance() {
System.out.println( danceMessage );
}
...
}
based on this you can implement Bear:
class Bear extends DanceAnimal {
public Bear() {
super( "Look MA' I am dancing" );
}
...
}
Lion and Tiger would just extend Animal base class and Food would probably be an enum.
My 2 cents on it.
Bear in mind that the above doesn't handle concurrency -- so one thread calling dance while the other calls play will corrupt the energyLevel -- but you can simply add a Lock around this.
class AnimalZoo {
public int energyLevel=0;
public String defaultVoice = "Grrr";
public String sing = "I am lovinig t";
public static void main(String [] args) {
Tiger t = new Tiger();
Lion l = new Lion();
Bear b = new Bear();
AnimalZoo az = new AnimalZoo();
az.soundOff();
}
public void soundOff() {
t.speak();
l.speak();
b.speak();
}
public void eat {
energyLevel+=8;
}
public void play{
energyLevel-=5;
}
public void speak {
energyLevel-=1;
}
}
}
class Lion extends AnimalZoo {
defaultVoice = "Don't you dare ask me";
System.out.printnln(defaultVoice);
}
class Tiger extends AnimalZoo {
public void speak {
defaultVoice = "ROARR";
System.out.println(defaultVoice);
}
}
class Bear extends AnimalZoo {
public void dance() {
System.out.println("Look MA' I am dancing");
}
Not all what you're looking for but something like this would help you to figure it out, d
eclare an animal class with all the properties and behavior common to all of them, then declare all the classes that define properties and behavior for each animal that extends the base class:
abstract class Animal
{
private int energyLevel;
public Animal(int eLevel)
{
setEnergyLevel(eLevel);
}
public void setEnergyLevel(int eLevel)
{
this.energyLevel = eLevel;
if(energyLevel <= 0)
{
energyLevel = 0;
}
}
public void speak()
{
System.out.println("grr...");
setEnergyLevel(energyLevel - 1);
}
public void eat()
{
setEnergyLevel(energyLevel + 8);
}
public void play()
{
System.out.println("I'm loving it...");
setEnergyLevel(energyLevel - 5);
}
}
class Lion extends Animal
{
public Lion(int energy)
{
super(energy);
}
}
class Bear extends Animal
{
public Bear(int energy)
{
super(energy);
}
public void dance()
{
System.out.println("I am dancing...");
}
}
class Tiger extends Animal
{
public Tiger(int energy)
{
super(energy);
}
}
Try the following code:
public abstract class Animal {
int energyLevel = ZooAnimalConstants.DEFAULT_ENERGY;
String speakMessage;
String playMessage;
public Animal(int energy) {
this.setEnergyLevel(energy);
speakMessage="Grr...";
playMessage="I am loving it";
}
public void setEnergyLevel(int energy) {
if (energy < 0) {
this.energyLevel = ZooAnimalConstants.DEFAULT_ENERGY;
} else {
this.energyLevel = energy;
}
}
public void speak() {
System.out.println("speak message : "+speakMessage);
this.setEnergyLevel(this.energyLevel - ZooAnimalConstants.MINUS_SPEAK_ENERGY);
}
public void eat() {
this.setEnergyLevel(this.energyLevel + ZooAnimalConstants.ADD_EAT_ENERGY);
}
public void play() {
System.out.println("play message : "+playMessage);
this.setEnergyLevel(this.energyLevel - ZooAnimalConstants.MINUS_PLAY_ENERGY);
}
public int getEnergyLevel()
{
return this.energyLevel;
}
}
Dance Behaviour:
public interface DanceBehaviour {
public abstract void dance();
}
public class Bear extends Animal implements DanceBehaviour{
public Bear() {
super(ZooAnimalConstants.BEAR_DEFAULT_ENERGY);
}
public void dance()
{
System.out.println("Look MA' I am dancing");
}
#Override
public String toString() {
// TODO Auto-generated method stub
return "Bear";
}
}
public class Tiger extends Animal {
public Tiger() {
super(ZooAnimalConstants.TIGER_DEFAULT_ENERGY);
super.speakMessage = "ROARR ..";
}
#Override
public String toString() {
// TODO Auto-generated method stub
return "Tiger";
}
}
public class Lion extends Animal {
public Lion() {
super(ZooAnimalConstants.LION_DEFAULT_ENERGY);
this.speakMessage = "Don't you dare ask me";
}
#Override
public String toString() {
// TODO Auto-generated method stub
return "Lion";
}
}
import java.util.ArrayList;
public class Zoo {
ArrayList<Animal> listAnimal;
public Zoo()
{
listAnimal= new ArrayList<Animal>();
}
public void addAnimal(Animal animal)
{
if(null != listAnimal && null != animal)
{
listAnimal.add(animal);
}
}
public void soundOff()
{
for(Animal animal : listAnimal)
{
animal.speak();
}
}
public void printAnimalEnergyReport()
{
for(Animal animal : listAnimal)
{
System.out.println("Energy Level for : "+animal.toString() + " : "+animal.energyLevel);
}
}
}
public interface ZooAnimalConstants {
int DEFAULT_ENERGY=0;
int ADD_EAT_ENERGY = 8;
int MINUS_SPEAK_ENERGY = 1;
int MINUS_PLAY_ENERGY = 5;
int TIGER_DEFAULT_ENERGY=5;
int LION_DEFAULT_ENERGY=6;
int BEAR_DEFAULT_ENERGY=4;
}
public class ZooDemoMain {
public static void main (String args[])
{
Zoo myZoo = new Zoo();
Animal tiger = new Tiger();
Animal bear = new Bear();
Animal lion = new Lion();
myZoo.addAnimal(tiger);
myZoo.addAnimal(lion);
myZoo.addAnimal(bear);
myZoo.soundOff();
myZoo.printAnimalEnergyReport();
bear.setEnergyLevel(0);
if(bear.getEnergyLevel() == 0)
{
bear.eat();
myZoo.printAnimalEnergyReport();
}
}
}
This question has been asked in a C++ context but I'm curious about Java. The concerns about virtual methods don't apply (I think), but if you have this situation:
abstract class Pet
{
private String name;
public Pet setName(String name) { this.name = name; return this; }
}
class Cat extends Pet
{
public Cat catchMice() {
System.out.println("I caught a mouse!");
return this;
}
}
class Dog extends Pet
{
public Dog catchFrisbee() {
System.out.println("I caught a frisbee!");
return this;
}
}
class Bird extends Pet
{
public Bird layEgg() {
...
return this;
}
}
{
Cat c = new Cat();
c.setName("Morris").catchMice(); // error! setName returns Pet, not Cat
Dog d = new Dog();
d.setName("Snoopy").catchFrisbee(); // error! setName returns Pet, not Dog
Bird b = new Bird();
b.setName("Tweety").layEgg(); // error! setName returns Pet, not Bird
}
In this sort of class hierarchy, is there any way to return this in a way that doesn't (effectively) upcast the the object type?
If you want to avoid unchecked cast warnings from your compiler (and don't want to #SuppressWarnings("unchecked")), then you need to do a little more:
First of all, your definition of Pet must be self-referential, because Pet is always a generic type:
abstract class Pet <T extends Pet<T>>
Secondly, the (T) this cast in setName is also unchecked. To avoid this, use the "getThis" technique in the excellent Generics FAQ by Angelika Langer:
The "getThis" trick provides a way to
recover the exact type of the this
reference.
This results in the code below, which compiles and runs without warnings. If you want to extend your subclasses, then the technique still holds (though you'll probably need to genericise your intermediate classes).
The resulting code is:
public class TestClass {
static abstract class Pet <T extends Pet<T>> {
private String name;
protected abstract T getThis();
public T setName(String name) {
this.name = name;
return getThis(); }
}
static class Cat extends Pet<Cat> {
#Override protected Cat getThis() { return this; }
public Cat catchMice() {
System.out.println("I caught a mouse!");
return getThis();
}
}
static class Dog extends Pet<Dog> {
#Override protected Dog getThis() { return this; }
public Dog catchFrisbee() {
System.out.println("I caught a frisbee!");
return getThis();
}
}
public static void main(String[] args) {
Cat c = new Cat();
c.setName("Morris").catchMice();
Dog d = new Dog();
d.setName("Snoopy").catchFrisbee();
}
}
How about this old trick:
abstract class Pet<T extends Pet>
{
private String name;
public T setName(String name) { this.name = name; return (T) this; }
}
class Cat extends Pet<Cat>
{
/* ... */
}
class Dog extends Pet<Dog>
{
/* ... */
}
No, not really. You could work around it by using covariant return types (thanks to McDowell for the correct name):
#Override
public Cat setName(String name) {
super.setName(name);
return this;
}
(Covariant return types are only in Java 5 and above, if that's a concern for you.)
It's a bit convoluted, but you can do this with generics:
abstract class Pet< T extends Pet > {
private String name;
public T setName( String name ) {
this.name = name;
return (T)this;
}
public static class Cat extends Pet< Cat > {
public Cat catchMice() {
System.out.println( "I caught a mouse!" );
return this;
}
}
public static class Dog extends Pet< Dog > {
public Dog catchFrisbee() {
System.out.println( "I caught a frisbee!" );
return this;
}
}
public static void main (String[] args){
Cat c = new Cat();
c.setName( "Morris" ).catchMice(); // error! setName returns Pet, not Cat
Dog d = new Dog();
d.setName( "Snoopy" ).catchFrisbee(); // error! setName returns Pet, not Dog
}
}
public class Pet<AnimalType extends Pet> {
private String name;
public AnimalType setName(String name) {
this.name = name; return (AnimalType)this;
}
}
and
public class Cat extends Pet<Cat> {
public Cat catchMice() {return this;}
public static void main(String[] args) {
Cat c = new Cat().setName("bob").catchMice();
}
}