/* Monster.java example code AC Chapin */ package MonsterInherit; /** * class representing a general monster with teeth * */ public class Monster { protected int teeth; // protected means inheriting classes can use directly (and rest of package too) protected String name; protected boolean alive; /** * default constructor for Monster * */ public Monster() { setTeeth(0); setName("Monster"); setAlive(true); } /** * param constructor for Monster * */ public Monster(String name, int teeth) { this(); setTeeth(teeth); setName(name); } /** * param constructor for Monster * */ public Monster(String name, int teeth, boolean alive) { this(name, teeth); setAlive(alive) } /** * copy constructor for Monster * */ public Monster(Monster original) { this(); if (original != null) { // ints and Strings are safe to just copy values // if it was an object, we'd need to make a copy of that // with clone if possible setTeeth(original.getTeeth()); setName(original.getName()); setAlive(original.getAlive()); } } /** * accessor for teeth * */ public int getTeeth() { return teeth; } /** * mutator for teeth * */ public void setTeeth(int val) { teeth = val; } /** * accessor for alive --alternative naming style for boolean accessor * */ public boolean isAlive() { return alive; } /** * mutator for alive * */ public void setAlive(boolean alive) { this.alive = alive; } /** * accessor for name * */ public String getName() { return name; } /** * mutator for name * */ public void setName(String name) { this.name = name; } /** * monsters eat people * */ public void eat() { System.out.println("Eat people!"); } /** * monsters make noise * */ public void roar() { System.out.println("GRRARGH!"); } /** * monsters fight!! * */ public void fight(Monster other) { if (other != null) { System.out.println(this + " and " + other + " are fighting!"); roar(); other.roar(); // 50/50 chance if (Math.random() > 0.5) { System.out.println(other + " wins"); this.alive = false; } else { System.out.println(this + " wins"); other.alive = false; } } else { System.out.println(this + " and has no one to fight!"); roar(); } } /** * clone method - gives us polymorphic way to call right copy constructor * */ public Monster clone() { return new Monster(this); } /** * overrides toString from Object * * @return string representing this monster includes Object's version of * toString for fun * */ @Override public String toString() { return name + " -- a monster with " + getTeeth() + " teeth at " + super.toString(); // use Object's toString } /** * check if equal to obj * overriding equals from Object, so take Object */ public boolean equals(Object obj) { // only other monsters if (obj instanceof Monster) { // cast to get access to vars Monster mobj = (Monster)obj; // check all vars // taking advantage of short circuit evaluation of && so no null pointer // wouldn't have to do that check if my name mutator check for nulls return mobj.getTeeth() == getTeeth() && mobj.alive == alive && getName() != null && getName().equals(mobj.getName()) ; } return false; } /** * check if equal to obj * alternate version using reflection */ public boolean equals(Object obj) { // only other monsters of same type if (obj != null && obj.getClass() == getClass()) { // cast to get access to vars Monster mobj = (Monster)obj; // check all vars // taking advantage of short circuit evaluation of && so no null pointer // wouldn't have to do that check if my name mutator check for nulls return mobj.getTeeth() == getTeeth() && mobj.alive == alive && getName() != null && getName().equals(mobj.getName()) ; } return false; } } /* **Why does equals HAVE to take Object? ******************************* // it is tempting to try to do equals with overloading // but this falls apart because of how overloaded methods // interact with inheritance and polymorphsim: // Consider this polymorphic code that would work fine with the correct code above // but work not work with the overLOADED versions of equals below public static void testEquality() { Object m1 = new Monster(); Monster m2 = new Monster(); // with correct code: works // version 1 below: calls equals(Object) since m1 officially an Object // reports false even though same values // version 2 below: calls equals(Monster) so that much works... BUT: System.out.println(m2.equals(m1)); Monster m3 = new MuppetMonster("Biff", "Song1"); MuppetMonster m4 = new MuppetMonster("Biff", "Song2"); // with correct code: works // with version 1 or 2: calls equals(Monster) instead of equals(MuppetMonster) because m3 is officially a Monster // so reports they are equal despite different songs System.out.println(m4.equals(m3)); } // IN MONSTER version 1----------------------------------------- // overload, Monster only public boolean equals(Monster mobj) { return mobj.getTeeth() == getTeeth() && mobj.alive == alive && getName() != null && getName().equals(mobj.getName()) ; } // but this one is called if variable is superclass type // e.g. Object m = new Monster(); public boolean equals(Object obj) { return false; } // IN MONSTER version 2----------------------------------------- // overload, Monster only public boolean equals(Monster mobj) { return mobj.getTeeth() == getTeeth() && mobj.alive == alive && getName() != null && getName().equals(mobj.getName()) ; } // fixes version 1 problem, but then breaks for subclasses public boolean equals(Object obj) { if (obj instanceof Monster) { return equals((Monster)obj); } return false; } //IN MUPPETMONSTER----------------------------------------- // override Object version public boolean equals(Object obj) { if (obj instanceof MuppetMonster) { return equals((MuppetMonster)obj); } return false; } // overload, MuppetMonster only public boolean equals(MuppetMonster mobj) { return super.equals(mobj) && getMySong() != null && getMySong().equals(mobj.getMySong()) ; } // but we STILL have the Monster version, so we have three equals methods now // and they are called based on VARIABLE not OBJECT type ************************************************* */