07. Java Inheritance (Part 2)

More About Inheritance in Java

Casting a class:

    • You can only cast within an inheritance hierarchy (up or down). For example, you can't cast a Circle as a Parent.
    • Use the instanceof operator to check a hierarchy before casting down from a parent to a child class (as shown above).
    • If you downcast an object variable that's NOT of the specified subclass (or lower), an exception will be thrown!

Final classes cannot be parent classes. To define the syntax:

final class ClassName

Final methods cannot be overriden. To define a method as final, put the word final just before the return type of the method. A compiler error will occur if you attempt to override (define with the same name) a method in a subclass. Example: public final void myFinalMethod()

/*
Exercise 7.1: Write the UML diagram first, then write the class in Java for the Hourly class (details below).
 The superclass given here is called Employee. One subclass of Employee (called Salaried).
  Write another subclass of Employee called Hourly. The Hourly class has a private instance variable for hourly rate. 
  Include a constructor, accessor method, mutator method (for the hourly rate) and toString method. See the Salaried class as an example.
 */
public class Exercise_7_1_Employee
{
 protected String name;
 protected String ssn;
 public Exercise_7_1_Employee( String n, String s )
 {
      name = n; ssn = s;
 }
 
 public String getName()
 {
  return name;
 }
 
 public String getSsn()
 {
  return ssn;
 }
 // could add mutators 
 
 public String toString()
 {
  return "Employee: name=" + name + ", SSN= " + ssn;
 }
}

public class Exercise_7_1_Salaried extends Exercise_7_1_Employee
{
 private double salary;
 public Exercise_7_1_Salaried(String name, String ssn, double s)
 {
      super(name, ssn);
      salary = s;
 }
 public void setSalary( double sal )
 {
  salary = sal;
 }
 public double getSalary(){ return salary; }
 public String toString()
 {
      return "Salaried " + super.toString()+ ", Salary=" + salary;
 }
} // end class Salaried

public class Exercise_7_1_Hourly extends Exercise_7_1_Employee
{
 private double hourlyRate;
 
 public Exercise_7_1_Hourly(String name, String ssn, double d_hourlyRate)
 {
      super(name, ssn);
      hourlyRate = d_hourlyRate;
 }
 public void setHourlyRate( double d_HourlyRate )
 {
  hourlyRate = d_HourlyRate;
 }
 public double getHourlyRate()
 {
  return hourlyRate;
 }
 public String toString()
 {
      return "Hourly Rate " + super.toString() + ", Hourly Rate=" + hourlyRate;
 }
 
 public static void main(String[] args)
 {
  Exercise_7_1_Hourly oExercise_7_1_Hourly = new Exercise_7_1_Hourly("ABC", "111-222-3333", 20);
  System.out.println(oExercise_7_1_Hourly.toString());
 }
}

Object class

Object Class (java.lang.Object):

  • The Object class is the common ancestor class of ALL classes, including arrays and programmer-defined classes.
  • A programmer-defined class implicitly is a subclass of Object unless explicitly declared as a subclass of another class.
  • A variable declared as Object may refer to ANY class instance, including arrays (but not primitive types), for example:

Object obj1, obj2;

String str="Some String";

Color col= new Color(0xAABBCC);

obj1 = str;

obj2 = col;

  • An Object reference is more commonly used to make a method "generic", for example:

public class Node { // Generic node of a linked list

private Object data; // instead of C/C++ void*

private Node next;

public Node( Object o, Node n ){

data = o; next = n;

}

. . .

} // end class Node

    • Every method of Object is inherited by all other classes. Some of these methods should be overridden by customized definitions of the methods.
    • Some of the Object methods:
    • Object clone( ) - returns a copy of the object for which the method is called. The value of every field (instance variable) is duplicated. If the object contains a reference to another object, the contained object is not duplicated. This method is declared: protected native Object clone( ) throws CloneNotSupportedException.
    • boolean equals( Object ) - returns true if the two objects are considered equal, falseotherwise. The default implementation compares just the object references. Many subclasses override this method so that the values of the instance variables are compared.
    • void finalize( ) - this method is called by the garbage collector before destroying the object. By default, this method does nothing, but this method may be overridden. This method is declared: protected void finalize( ) throws Throwable (exceptions will be discussed in the last week).
    • Class getClass( ) - returns an object reference of the Class class, which can be used to obtain run-time type information about an object. For example (assume objVar has been declared and initialized to refer to an object):
        • Class c = objVar.getClass();
        • String className = c.getName();
        • Class sup = c.getSuperclass();
    • int hashCode( ) - returns an integer suitable for determining the hash code for an object. Hash codes are used to locate the entries for objects in hash tables. The JVM uses hash tables for random access to objects.
    • String toString( ) - returns a String representation of an object. When a String object is concatenated to an object reference, the object's toString method is called. Similarly, when an object reference is passed to System.out.print or System.out.println, the object's toString method is called. The default toString method just returns a string with the object's class name and the hexadecimal representation (value returned by the hashCode() method) of the object. Therefore, this method is usually overridden by its subclasses.
// toString Examples
public class Customer {
  private String name="";
  private long acctNum;
  private SavingsAccount savings=new SavingsAccount();
     .
     . (constructors & other methods here)
     .
  public String toString(){
          return "Customer: name=" + name +
              ", Acct#=" + acctNum + ", savings="
              savings;// calls savings.toString()
  }
} // end class Customer
// The following would be in another class in a method
     Customer cust=new Customer("a name", "a #", 500.);
     System.out.println(cust);// calls cust.toString()

Abstract Classes

A class with an abstract method is an abstract class

    • An abstract class CANNOT be instantiated
    • An abstract class may only be used an a superclass
    • All abstract methods MUST be overridden in its subclass(es) (or your subclass MUST be abstract)
    • Syntax to make a class abstract is to put the keyword abstract before the word class in the class' heading, for example:

public abstract class Employee {

  • Syntax to write an abstract (non-static) method is to put the keyword abstract before the return type, and to have only a semi-colon (;) for the body (after the right parenthesis of the parameters), for example:

public abstract double computePay( );

/*Exercise 7.2: Using the example code for the abstract Employee class. Write a subclass of the abstract Employee class called Hourly, which includes private instance variables for hours worked and hourly rate, and a constructor that includes parameters to initialize those as well as the superclass variables. Don't forget to override the computePay method.

- Write a driver program that will contain an array Employee objects, initialized to instances of
 Hourly and Salaried shown below.Then write a loop to print the toString AND return value of computePay
 on each Employee array element (you DON'T need to check if Salaried or Hourly!)
 */
public abstract class Exercise_7_2_Employee
{
 protected String name;
 
 protected String ssn;
 
 public Exercise_7_2_Employee( String n, String s )
 {
      name = n; ssn = s;
 }
 
 public String getName(){ return name; }
 
 public String getSsn(){ return ssn; }
   // could add mutators
 
 public abstract double computePay();
 
} // end abstract class Employee
 
public class Exercise_7_2_Salaried extends Exercise_7_2_Employee
{
 private double salary;
 private static int payPeriods = 12;
 
 public Exercise_7_2_Salaried( String name, String ssn, double s)
 {
      super(name, ssn);
      salary = s;
 }
 public void setSalary( double sal ){ salary = sal; }
 
 public double getSalary(){ return salary; }
 
 public double computePay()
 {
      return Math.round(salary/payPeriods*100.)/100.;
 }
 
 public String toString()
 {
      return "Salaried: \n name = " + name + ",\n SSN = " + ssn + ",\n Salary = " + salary;
 }
} // end class Salaried
 
public class Exercise_7_2_Hourly extends Exercise_7_2_Employee
{
 private double d_HoursWorked = 0;
 private double d_HourlyRate = 0;
 
 public Exercise_7_2_Hourly( String name, String ssn, double hoursWorked, double hourlyRate)
 {
      super(name, ssn);
      d_HoursWorked = hoursWorked;
      d_HourlyRate = hourlyRate;
 }
 
 public void setHoursWorked(double hoursWorked) { d_HoursWorked = hoursWorked; } 
 public double getHoursWorked(){ return d_HoursWorked; }
 public void setHourlyRate(double hourlyRate){ d_HourlyRate = hourlyRate; } 
 public double getHourlyRate(){ return d_HourlyRate; }
 public double computePay()
 {
      return Math.round(d_HoursWorked * d_HourlyRate * 100.0)/100.0;
 }
 
 public String toString()
 {
      return "Hourly:\n name = " + name + ",\n SSN = " + ssn + ",\n Hours Worked = " + d_HoursWorked + ",\n Hourly Rate = " + d_HourlyRate;
 }
}
 
public class Exercise_7_2_Test
{
 public static void main(String[] args)
 {
  Exercise_7_2_Employee [] empArray = new Exercise_7_2_Employee[] {
            new Exercise_7_2_Hourly("Donald Duck", "123-45-6789", 20., 30.),
            new Exercise_7_2_Salaried("Mickey Mouse", "987-65-4321", 100000.),
            new Exercise_7_2_Salaried("Bugs Bunny", "121-21-2121", 90000.),
            new Exercise_7_2_Hourly("Elmer Fudd", "343-43-4343", 10.97, 15.25)
            };
  
  for (int i = 0; i < empArray.length; i++)
  {
   System.out.println(empArray[i].toString() + "\n Computed Pay: " + empArray[i].computePay());
  }
 }
}

Interfaces

Interfaces indicate only the "design features" (method access, return value and parameters) of a class. Therefore, interfaces do not have any instance variables, and the methods have no body. They are primarily used to indicate how to communicate with an object. Interfaces are sometimes used to simulate multiple inheritance.

    • All methods in an interface are abstract, so they have no definition (body), and therefore, you cannot instantiate an interface, but a class may only implement an interface.
    • An interface may have variables, but they are assumed to be static and final (cannot be non-static)
    • A class may implement more than one interface (which somewhat emulates multiple inheritance)
    • When a class implements an interface, it MUST define all the methods of the interface; if not, the class must be declared abstract
    • An interface may be a subclass of another interface (using extends)
    • An interface that is entirely empty is a marker interface. A class that implements a marker interface provides additional information about itself. An example of a marker interface is Cloneable in java.lang, which identifies a class as one that will allow itself to be cloned by the clone() method of the Object class.

Syntax to define an interface:

interface InterfaceID {

// static-final variable declarations

// method declarations (no body)

}

    • Optional access-specifier: public (before the keyword interface)
    • Method declarations are assumed to be abstract, so you don't need the abstract keyword (ok to use)

Syntax to implement an interface:

class ClassID implements InterfaceID {

    • Optional access-specifier: public (before the keyword class)
    • InterfaceID may be a list of interface identifiers separated by a comma
    • You may declare an interface variable, but it can only refer to a instance of a class which implements the interface, for example (using interfaces & class below):

Choice ch = new Choice(); ItemSelectable is=ch;

(in TryChoiceApplet in the link below) passing this to ItemListener arg

/*Exercise 7.3: Write an interface called Comparable that has an instance method that returns an int called compareTo with 1
 Object parameter. (Note that there is a library interface for this, but write it as an exercise.) 
 Modify the Customer class from Chapter 5 to implement this interface. 
 Override the compareTo (be sure to cast the parameter appropriately) to first compare the names,
 but if they're the same, compare the account numbers, and if they're the same, compare the saving's balances.
  Use the compareToIgnoreCase instance method of the String class for the names, and return the result of that if not 0.
  When and if comparing the account numbers, if they're not the same, return -1 if this' account number < parameter's account number, otherwise return 1.
  When and if comparing the saving's balances, if equal, return 0, or return -1 if this' saving's balance < parameter's saving's balance, otherwise return 1.
*/
public interface Exercise_7_3_Comparable
{
 public int compareTo(Object obj);
}
public class Exercise_7_3_Customer implements Exercise_7_3_Comparable
{
 private String s_CustomerName;
 private long l_CustomerAcctNum;
 private Exercise_5_3_SavingsAccount oExercise_5_3_SavingsAccount;
 
 public Exercise_7_3_Customer()
 {
  oExercise_5_3_SavingsAccount = new Exercise_5_3_SavingsAccount();  // in case, might prevent exception
 }
 public Exercise_7_3_Customer(String customerName, long customerAcctNum, double balance)
 {
  s_CustomerName = customerName;
  l_CustomerAcctNum = customerAcctNum;
  oExercise_5_3_SavingsAccount = new Exercise_5_3_SavingsAccount(balance);
 }
 
 public String getCustomerName()
 {
  return s_CustomerName;
 }
 
 public long getCustomerAcctNum()
 {
  return l_CustomerAcctNum;
 }
 
 public Exercise_5_3_SavingsAccount getSavingsAccount()
 {
  return oExercise_5_3_SavingsAccount;
 }
 
 public void setCustomerName(String CustomerName)
 {
  s_CustomerName = CustomerName;       // is it require to check for empty string ??
 }
 
 public void display()
 {
  System.out.println("Customer Name: " + s_CustomerName);
  System.out.println("Customer A/C no: " + l_CustomerAcctNum);
  System.out.println("Customer Balance: " + oExercise_5_3_SavingsAccount.getBalance());
 }
 
 // Interface method definition
 public int compareTo(Object obj)
 {
  if (obj instanceof Exercise_7_3_Customer)
  {
   Exercise_7_3_Customer oCustomer = (Exercise_7_3_Customer)obj;
   int i_compareName;
   //int i_compareAccNum, i_compareSavingBal; // Not needed
   // get the results of comparing each instance variable of this and param.
   i_compareName = this.s_CustomerName.compareToIgnoreCase(oCustomer.s_CustomerName);
   
   if(i_compareName == 0)
   {
    if(this.l_CustomerAcctNum < oCustomer.l_CustomerAcctNum)
     return -1;
    else if(this.l_CustomerAcctNum > oCustomer.l_CustomerAcctNum)
     return 1;
    else // if a/c number is equal
    {
     if(this.getSavingsAccount().getBalance() < oCustomer.getSavingsAccount().getBalance())
      return -1;
     if(this.getSavingsAccount().getBalance() > oCustomer.getSavingsAccount().getBalance())
      return 1;
     else
      return 0;   // object is equal
    }
   }
   else
   {
    return i_compareName;
   }
  }
  else         // parameter not a Customer class object
   return this.compareTo(obj);  // calls Object's compareTo? --> Don't get it (copied from professor's answer in forum reply) -> it will become recursive
 }   // end compareTo
 
 public static void main(String[] args)
 {
  // Test case
  // 1st object
  Exercise_7_3_Customer oExercise_7_3_Customer1 = new Exercise_7_3_Customer("ABC", 1234567890, 1000.00); oExercise_7_3_Customer1.display();
  oExercise_7_3_Customer1.oExercise_5_3_SavingsAccount.transaction(500); oExercise_7_3_Customer1.display();
  // 2nd object
  Exercise_7_3_Customer oExercise_7_3_Customer2 = new Exercise_7_3_Customer("ABCDE", 1234567890, 1000.00); oExercise_7_3_Customer2.display();
  oExercise_7_3_Customer2.oExercise_5_3_SavingsAccount.transaction(500); oExercise_7_3_Customer2.display();
  // Test interface
  System.out.println("Comparision result: " + oExercise_7_3_Customer1.compareTo(oExercise_7_3_Customer2));
 }
}

Wrapper Classes

Wrapper classes are for creating objects that represent primitive types. They are used when an object is required, but the data is of a primitive type. The wrapper classes are in the java.lang package and are similar to the primitive type names, except they start with a capital letter: Boolean, Character,Byte, Short, Integer, Long, Float and Double.

To instantiate a wrapper object: new WrapperClassName( primitive-type-value )

where the primitive-type-value MUST be the same type as what the wrapper class represents (e.g., pass an int value to Integer constructor) OR a String with the characters that represent the same type (except NOT with Character).

Examples:

Double doubObj = new Double( 3.45 );

Integer intObj = new Integer( "9876" );

Note: You may pass a double to the Float constructor.

Wrapper Class Constants & Methods:

There are many methods and constants in each wrapper class that you may look up here: http://docs.oracle.com/javase/7/docs/api/

for example, MAX_VALUE, MIN_VALUE (constants), compareTo (method) in the numeric classes.

Every wrapper class (except Boolean & Character) has the following methods to convert back to primitive types:

    • public byte byteValue() - Returns the value of this object as a byte.
    • public short shortValue() - Returns the value of this object as a short.
    • public int intValue() - Returns the value of this object as an int.
    • public long longValue() - Returns the value of this object as a long.
    • public float floatValue() - Returns the value of this object as a float.
    • public double doubleValue() - Returns the value of this object as a double.

Examples of calls:

double dnum;

dnum = doubObj.doubleValue() + intObj.doubleValue();

Long longObj = new Long( intObj.longValue() + 50L );

AUTOMATIC BOXING AND UNBOXING:

  • Boxing is converting a primitive value to its equivalent wrapper object. Unboxing is the reverse conversion. Java allows the automatic boxing and unboxing, for example:

Double dObject = 7.8; // autoboxing of 7.8, assigned new Double(7.8)

double result = dObject * 100.0 ; // auto-unboxing of dObject

/*Exercise 7.4: Add to the Node class in this Catalyst lesson on page 7.6
  to include an accessor that returns the data (Object), a mutator for the data and a mutator for the next.
 Write a static method that reads an integer from the user (prompt first),
  then instantiates a new Node passing an Integer with the input value (and null for next).  Return that Node.
 Write main to call the method, then call the accessor on the returned Node to get the data.  
  Multiply the data by 2, then instantiate a new Node passing the data multiplied by 2 and
  the node returned from the static method (as the next). RUN THIS program.
  Turn in the class and output (copied and pasted at the end of the .java file, commented out).
 */
import java.util.Scanner;
public class Exercise_7_4_Node
{
 static Scanner oScanner = new Scanner(System.in);
 private Object data;      // instead of C/C++ void*
 private Exercise_7_4_Node next;
 
 public Exercise_7_4_Node()
 {
 }
 
 public Exercise_7_4_Node( Object o, Exercise_7_4_Node n )
 {
  data = o; 
  next = n;
 }
 
 public Object getData()
 {
  return data;
 }
 
 public void setData(Object obj)
 {
  data = obj;
 }
 
 public void setNext(Exercise_7_4_Node oNext)
 {
  next = oNext;
 }
 
 public String displayNode()
 {
  return "Data: " + data + " Next: " + next;
 }
 
 public static Exercise_7_4_Node getNodeValueFromuser()
 {
  Exercise_7_4_Node oNode = new Exercise_7_4_Node();
  System.out.println("Enter the node value");
  Integer oInt = oScanner.nextInt();     // Wrapper class object integer
  oNode.setData(oInt);
  oNode.setNext(null);
  return oNode;
 }
 
 public static void main(String[] args)
 {
  Exercise_7_4_Node oNode1 = Exercise_7_4_Node.getNodeValueFromuser();
  Integer oInt = (Integer) oNode1.getData();
  Exercise_7_4_Node oNode2 = new Exercise_7_4_Node((2 * oInt), oNode1);
  System.out.println("Node1: " + oNode1.displayNode());
  System.out.println("Node2: " + oNode2.displayNode());
  System.out.println("Node1 is same as Node2's next: " + oNode1);
 }
}
/*
Output:
Enter the node value
10
Node1: Data: 10 Next: 5null
Node2: Data: 20 Next: 5Exercise_7_4_Node@500fbfa2
Node1 is same as Node2's next: Exercise_7_4_Node@500fbfa2
*/