Inheritance and other more advanced object-oriented programming concepts

From Knowledge Kitchen
Jump to navigation Jump to search


Object-oriented design principles

The pillars of object-oriented design:

  • abstraction - one should be able to use a well-designed class as a black box
  • encapsulation - each class or object has properties and methods that "belong to" it.
  • inheritance - classes can inherit the properties and methods of a "parent" class
  • polymorphism - objects of different types can each respond to the same commands. variables typed as being of a super-class can actually hold sub-class types in them.

Additional best practices:

  • cohesion
  • coupling
  • separation of concerns

Special properties

  • this - refers the current object, where relevant
  • super - refers to a given class's parent class.

The elements of good object-oriented style

  • instance properties should be private
    • make setters and getters if you want to allow external access to these properties
    • do validation in the setters, where appropriate
    • call those getters and setters everywhere you need to get or set the value of those properties
  • use the 'this' keyword as a prefix when referring to instance properties and methods from within the same class
  • use the class name as a prefix when referring to static properties and methods

Inheritance example

The following classes are used together to model a Mammal, and sub-classes Dog and Human.

  • This example focuses on inheritance-related techniques of object-oriented programming.

Mammal

package edu.nyu.cs.fb1258;

/**
 * A virtual representation of a Mammal
 * @author Foo Barstein
 * @version 2.9
 *
 */
public class Mammal {
	/**
	 * constant class attribute representing male gender.
	 * This makes it easy to refer to male or female gender by intuitive names, rather than integers
	 */
	final static int MALE = 0;
	
	/**
	 * constant class attribute representing female gender.
	 * This makes it easy to refer to male or female gender by intuitive names, rather than integers
	 */
	final static int FEMALE = 1;
	
	/**
	 * whether or not  a given mammal has mammary glands
	 */
	boolean hasMammaryGlands = true;
	
	/**
	 * whether or not a given mammal is warm blooded
	 */
	boolean isWarmBlooded = true;
	
	/**
	 * whether or not a given mammal has hair
	 */
	boolean hasHair = true;
	
	/**
	 * the gender of a given mammal: 0 represents male, 1 represents female
	 */
	private int gender;
	
	/**
	 * a method representing the action of eating
	 */
	public void eat() {
		System.out.println("mammal eating... yum");
	}
	
	/**
	 * a method representing the action of sleeping
	 */
	public void sleep() {
		System.out.println("mammal sleeping zzzzzz.");
	}
	
	/**
	 * a method representing the action of lactating
	 */
	public void lactate() {
		System.out.println("mammal lactating...");
	}
	
	/**
	 * An overloaded lactate() method, which takes a boolean argument
	 * @param aLot whether to lactate a lot or not
	 */
	public void lactate(boolean aLot) {
		System.out.println("mammal lactating a lot...");
	}

	/**
	 * no-args constructor randomly assigns the Mammal object's gender
	 */
	public Mammal() {
		//not sure what to do here
		//this.gender = Mammal.NEUTRAL;
		double rand = Math.random() * 2;
		int g = (int) rand;
		this.gender = g;		
	}
	
	/**
	 * overloaded constructor to explicitly assign the Mammal object's gender
	 * @param g integer representing this Mammal's gender: 0 represents male, 1 represents female
	 */
	public Mammal(int g) {
		this.gender = g;
	}	
	
	
	//accessor methods
	
	/**
	 * method to return the private gender property as an integer
	 * @return int representing the current object's gender: 0 represents male, 1 represents female
	 */
	public int getGender() {
		return this.gender;
	}
	
	/**
	 * overloaded accessor method to return the private gender property as a user-friendly string
	 * @return User-friendly String representing the current object's gender: 'male' or 'female'
	 */
	public String getGender(boolean userFriendly) {
		if (userFriendly) {
			switch (this.gender) {
			case Mammal.MALE:
				return "male";
			case Mammal.FEMALE:
				return "female";
			}
		}
		
		return "unknown";
	}
	
}


Dog

package edu.nyu.cs.fb1258;

/**
 * A virtual representation of a Dog, which is, of course, a type of Mammal
 * @author Foo Barstein
 * @version 2.9
 *
 */
public class Dog extends Mammal {
	/**
	 * the name of this Dog object
	 */
	private String name;

	/**
	 * Setter for the name property
	 * @param n the new name of this Dog 
	 */
	public void setName(String n) {
		if (n.length() > 0) {
			this.name = n;			
		}
	}
	
	/**
	 * no-args constructor simply calls the Mammal class's no-args constructor
	 */
	public Dog() {
		super(); //the Mammal class's constructor assigns a random gender to this Dog
	}
	
	/**
	 * overloaded constructor assigns a random gender and sets the Dog object's name
	 * @param n String name of the new Dog object
	 */
	public Dog(String n) {
		super(); //the Mammal class's constructor assigns a random gender to this Dog

		//set this dog's name using the setter designed for this purpose
		this.setName(n);
	}
	
	/**
	 * override the default Mammal sleep method with a Dog-specific sleep style.
	 */
	public void sleep() {
		super.sleep(); //calling the Mammal's version of the sleep method first
		
		//then doing some additional Dog type sleeping...
		System.out.println(this.name + " is sleeping ...woof whimper whoof.");
	}
	
}


Human

package edu.nyu.cs.fb1258;

/**
 * A virtual representation of a Human, which is, of course, a type of Mammal
 * @author Foo Barstein
 * @version 2.9
 *
 */
public class Human extends Mammal {
	/**
	 * the first name of this Human object
	 */
	private String firstName;
	
	/**
	 * the last name of this Human object
	 */
	private String lastName;

	/**
	 * no-args constructor simply calls the Mammal class's default constructor
	 */
	public Human() {
		super(); //call parent class's default constructor
	}
	
	/**
	 * overloaded constructor sets the Human object's first name
	 * @param firstName String of first name of the new Human object
	 */
	public Human(String firstName) {
		super();  //call parent class's no-args constructor to assign a random gender to this Human
		this.firstName = firstName;
		this.lastName = null; //no last name... this is redundant since the default value for un-assigned Strings is null
	}
	
	/**
	 * overloaded constructor sets the Human object's first and last names
	 * @param firstName String of first name of the new Human object
	 * @param gender int representing the Human's gender: 0=male, 1=female
	 */
	public Human(String firstName, int gender) {
		super(gender);  //call parent class's constructor and pass it the gender to assign to this Human
		
		//set some Human-specific properties
		this.firstName = firstName;
		this.lastName = null;
	}
	
	/**
	 * overloaded constructor that sets the Human object's first and last names
	 * @param firstName String of first name of the new Human object
	 * @param lastName String of last name of the new Human object
	 */
	public Human(String firstName, String lastName) {
		super();
		this.firstName = firstName;
		this.lastName = lastName;
	}

	/**
	 * overloaded constructor that sets the Human object's first and last names, as well as gender.
	 * @param firstName String of first name of the new Human object
	 * @param lastName String of last name of the new Human object
	 * @param gender int representing the Human's gender: 0=male, 1=female
	 */
	public Human(String firstName, String lastName, int gender) {
		super(gender);
		this.firstName = firstName;
		this.lastName = lastName;
	}

	/**
	 * override the default Mammal sleep method with a Human-specific sleep style.
	 */
	public void sleep() {
		super.sleep(); //calling the Mammal's version of the sleep method first

		//then doing some additional Human type sleeping...
		System.out.println(this.firstName + " is sleeping ...snore snore snore.");
	}
	
	/**
	 * A specifically human method that other Mammals don't have
	 */
	public void speak() {
		System.out.println(this.firstName + " is saying something interesting...");
	}

}


TestMammal

package edu.nyu.cs.fb1258;

/**
 * A program that creates a few Mammal, Human, and Dog objects, and plays around with them in order to exhibit some properties of inheritance and polymorphism in Java.
 * @author Foo Barstein
 * @version 2.9
 *
 */
public class TestMammal {

	/**
	 * Play around with our custom classes.
	 * @param args array of command line arguments
	 */
	public static void main(String[] args) {

		//create a mammal object
		Mammal geoff = new Mammal(Mammal.MALE);
		System.out.println("Geoff the mammal is " + geoff.getGender(true));

		//create a dog object
		Dog lassy = new Dog("Lassy");
		System.out.println("Lassy the Dog is " + lassy.getGender(true));
		lassy.sleep();

		//create a dog object
		Human rodrigo = new Human("Rodrigo", "Barstein", Mammal.MALE);
		System.out.println("Rodrigo the Human is " + rodrigo.getGender(true));
		rodrigo.sleep();
		rodrigo.eat();
		
		//it's possible to store our objects in an array of type Mammal since Dog and Human are descendants of the Mammal type and share the basic Mammal properties and methods
		Mammal[] myMammals = new Mammal[3];
		myMammals[0] = geoff;
		myMammals[1] = lassy;
		myMammals[2] = rodrigo;
		
		//loop through and call the sleep() method that all Mammal and Mammals and descendants of Mammals have
		for (int i=0; i<myMammals.length; i++) {
			//Mammals and all its sub-classes have a speak method, so this works:
			myMammals[i].sleep();
		}
		
		//polymorphism is easy!
		Mammal meAsAMammal = new Human("Amos", "Bloomberg");
		Human meAsAHuman = (Human) meAsAMammal; //cast to the more specific type
		
		//more polymorphism
		Mammal spotAsAMammal = new Dog("Spot");
		Dog spotAsADog = (Dog) spotAsAMammal; //more casting
		spotAsADog.lactate(); //call the lactate method
		spotAsADog.lactate(true); //call an overlloaded version of the method that accepts a boolean argument
		
		//Human spotAsAHuman = (Human) spotAsADog; //can't cast to "sister" types... Dog and Human both inherit from Mammal, but Humans do not necessarily have all the methods that Dogs have
		
		//meAsAMammal.speak(); //this is not allowed!!!  Mammals do not have a speak method.
		meAsAHuman.speak(); //call a specifically Human method
		
	}

}


What links here