POLYMORPHISM, INTERFACES, COMPOSITION

TOPICS


·  Polymorphism

·  Abstract Classes

·  Interfaces

·  Composition

·  Programming Samples

·  Interfaces: Figure, Circle, Rectangle, Triangle

·  Composition: Person, Date, PersonalInfo

OUTLINE


1. Polymorphism
 

·  Definition: Polymorphism = A basic principle of OOD defined as the ability to use the same expressions to denote different operations (in other words, the ability to associate multiple meanings to the same method name. )

·  Polymorphism is a word of Greek origin that means having multiple forms.

·  Reminder (Inheritance):

·  An object of a subclass class has the type of the subclass class, and it also has the type of the superclass.

·  An object of a derived class can be plugged in as a parameter in place of any of its ancestor classes.

·  In a class hierarchy, several methods may have the same name and the same formal parameter list. Moreover, a reference variable of a class can refer to either an object of its own class or an object of its subclass. Therefore, a reference variable can invoke a method of its own class or of its subclass(es).

·  Polymorphism is implemented through a mechanism known as late binding, dynamic binding or run-time binding --> refer to the situation where the method that gets executed is determined at execution time, not at compile time.

·  Definition: Binding = Associating a method definition with its invocation.

·  In early binding, a methods definition is associated with its invocation when the code is compiled. In late binding, a method’s definition is associated with the method’s invocation at execution time. In late binding the method invoked is determined by the type of object to which the variable refers, NOT by the type of the reference variable.

·  NOTE: Except for a few special cases, Java uses late binding for all methods. Java uses late binding for all methods except for:

·  final methods (you can declare a method as final by using the keyword final. If a method of a class is declared final, it cannot be overridden with a new definition in a derived class. You might wish to make a method final if it has an implementation that should not be changed and it is critical to the consistent state of the object. Methods called from constructors should generally be declared as final. If a constructor calls a non-final method, a subclass may redefine that method with unexpected/unwanted results. NOTE: You can also declare a class as final using the keyword final. If a class is declared final, then no other class can be derived from this class. This is particularly useful, for example, when creating an immutable class)

·  private methods

·  static methods.

·  In a well designed OOP program, most methods will communicate with a superclass reference and let late binding and polymorphism determine which method to call. Such a program is called extensible because you can add new functionality by deriving new classes from the superclass without changing existing code.

·  Using late binding Example: method toString. If an appropriate toString method is defined for a class, then an object of that class can be output using  System.out.println( ); as in these examples:

Point somePoint = new Point( );
System.out.println(somePoint);
Box b = new Box();
System.out.println(b);

These statements work because of late binding. Because of late binding, the toString method from the Point class is used, not the toString from the Object class. Same for Box.

·  Understanding Polymorphism (Example):

public class Class_1 {
    public void method_1() {
        System.out.println("Class_1 output from method_1");
    }

    public void method_2() {
        System.out.println("Class_1 output from method_2");
    }

    public String toString() {
        return "toString() from Class_1";
    }
}

public class Class_2 extends Class_1 {
    public void method_2() {
        System.out.println("Class_2 output from method_2");
    }

    public String toString() {
        return "toString() from Class_2. No method_1 in Class_2. ";
    }
}

public class Class_3 extends Class_1 {
    public void method_1() {
        System.out.println("Class_3 output from method_1");
    }

    public String toString() {
        return "toString() from Class_3. No method_2 in Class_3.";
    }
}

public class Class_4 extends Class_3 {
    public void method_2() {
        System.out.println("Class_4 output from method_2");
    }

    public String toString() {
        return "toString() from Class_4. No method_1 in Class_4.";
    }
}

public class Test_Polymorphism {
    public static void main(String[] arg) {
        Class_1 [] someObj = {new Class_1(), new Class_2(), new Class_3(), new Class_4()};
        System.out.println("Class_1 = superclass: method_1, method_2, toString");
        System.out.println("Class_2 = subclass of Class_1: method_2, toString");
        System.out.println("Class_3 = subclass of Class_1: method_1, toString");
        System.out.println("Class_4 = subclass of Class_3: method_2, toString\n");
        for(int i = 0; i < someObj.length; i++) {
            System.out.println(someObj[i]);//toString() invoked
            someObj[i].method_1();
            someObj[i].method_2();
            System.out.println();
        }
    }
}
OUTPUT:
Class_1 = superclass: method_1, method_2, toString
Class_2 = subclass of Class_1: method_2, toString
Class_3 = subclass of Class_1: method_1, toString
Class_4 = subclass of Class_3: method_2, toString

toString() from Class_1
Class_1 output from method_1
Class_1 output from method_2

toString() from Class_2. No method_1 in Class_2.
Class_1 output from method_1
Class_2 output from method_2

toString() from Class_3. No method_2 in Class_3.
Class_3 output from method_1
Class_1 output from method_2

toString() from Class_4. No method_1 in Class_4.
Class_3 output from method_1
Class_4 output from method_2


2. Introduction to Abstract Classes:
 

·  Definition: Abstract method = a placeholder for a method that will be fully defined in a subclass. In other words, the definition of the method is postponed - the abstract method has only the heading without the body. The heading of an abstract method contains the reserved word abstract and ends with a semicolon in place of its body. The body of the method (the implementation) is defined in the derived classes. NOTE: Abstract methods cannot be private.

·  Examples:

public abstract void someMethod(int x, int y);
public abstract int computeSomething(int[] someArray);
public abstract void movePoint(double dX, double dY);

·  Definition: Abstract class = a class that is declared with the reserved word abstract in its heading. Also, any class that contains an abstract method is called an abstract class (see [3] below). A class that has no abstract methods is called a concrete class.

·  Rules and facts about abstract classes:

1. An abstract class can contain instance variables, constructors, finalizer, and nonabstract methods.
2. An abstract class can contain abstract method(s).
3. If a class contains an abstract method, then the class must be declared abstract.
4. If a subclass of an abstract class adds to or does not define all of the abstract methods, the subclass is abstract also, and must add
abstract to its modifier.
5. Because an abstract class is not fully defined, you cannot create objects of the abstract class type. Although an object of an abstract class cannot be created, it is perfectly fine to have a parameter of an abstract class type.
6. You can create objects of a subclass of an abstract class only if the subclass gives the definitions of all the abstract methods of the superclass.

·  Abstract classes are used as superclasses from which other subclasses within the same context can be derived. Abstract classes can only be used to derive more specialized classes. They can be used to force subclasses to provide certain methods.

·  NOTE: All of the methods in an interface (see Interfaces below) are implicitly abstract, so the abstract modifier is not used with interface methods (it could be; it's just not necessary).


3. Interfaces
 

·  Reminder: In Java a class can be derived from only one existing class because Java does not support multiple inheritance.

·  Reminder: If a class contains an abstract method, then the class must be declared abstract. Also, because an abstract class is not fully defined, you cannot create objects of the abstract class type.

·  Definition: Interface = a class that contains only abstract methods and/or named constants. An interface is a mechanism used by unrelated objects in order to interact with each other - like a protocol.

·  Java allows a class to implement more than one interface --> this is how Java implements multiple inheritance, which is not true multiple inheritance.

·  If a class implements an interface, it must provide definitions for each of the methods of the interface; otherwise, you cannot create objects of that class type (just like with abstract classes).

·  An interface is a reference type, similar to a class, that can contain only constants, method signatures, and nested types (no method bodies.)

·  Interfaces cannot be instantiated; they can only be implemented by classes or extended by other interfaces.

·  Basically, an interface is a list of methods that classes promise to implement (protocol). While inheritance gives you an is-a relationship and code-sharing, an interface gives you an is-a relationship without code sharing.

·  Defining an interface is similar to creating a new class. To use an interface, you have to write a class that implements the interface. When a class implements an interface, it provides a method body for each of the methods declared in the interface.

·  Interface declaration, general syntax:

public interface InterfaceName {
    public Type method1(Type param1, Type param2...);
    public Type method2(Type param1, Type param2...);
    ...
}

·  A class can declare that it implements an interface. This means the class contains an implementation for each of the abstract methods in that interface. Otherwise --> compiling error!

·  Implementing an interface, general syntax:

public class ClassName implements InterfaceName {
    definition/implementation for method1
    definition/implementation fpr method2
    ...
}

·   Example:

public interface Employee {
    public String getEmployeeID();
    public double getSalary();
    public void setEmployeeID(String id);
    public void setSalary(double salary);
}

public class Faculty implements Employee {...}

·  NOTE: A class can extend the definition of only one class (single inheritance), but it  can implement any number of interfaces. In fact, a class can both extend another class (inheritance) and implement one or more interfaces.  Example:

public class Person {...}

public interface Employee {
    public String getEmployeeID();
    public double getSalary();
    public void setEmployeeID(String id);
    public void setSalary(double salary);
}

public class Faculty extends Person implements Employee {...}

·  Why using interfaces?

·  Can declare a common set of methods that one or more classes are required to implement.

·  Can provide access to an object's programming interface without revealing the details of its class.

·  Can provide a relationship between unrelated classes without imposing an unnatural class relationship.

·  NOTE: Most likely, you will need to write your own interfaces when writing fairly complex applications. However, Java standard packages include a few interfaces that are used very often.

·  Take a look at the Figure interface and the classes Circle, Rectangle and Triangle implementing it.

·  Why do both interfaces and abstract classes exist in Java? An abstract class can do everything an interface can do and more. So why would someone ever use an interface? Answer: Java has single inheritance (a class can extend only one superclass, but can implement many interfaces). Having interfaces allows a class to be part of a hierarchy (polymorphism) without using up its inheritance relationship.


4. Composition
 

·  Effective software development relies on reusing existing code. The goal with OOP languages is to reuse classes without changing the code within the class - one OOP technique for code reuse is known as composition.

·  Definition: Composition = another way to relate two classes. In composition, one or more members of a class are objects of another class type. In other words, composition (aggregation) = the use of classes as instance variables in the definition of another class.

·  Composition is a “has-a” relation. Example: “Every person has a last name” , "Every point has a x-coordinate"

·  To understand how composition works, take a look at the program sample below.

·  Making a copy of an object requires a special method called a copy constructor.

·  Definition: Copy constructor = a constructor with a single argument of the same type as the class. The copy constructor should create an object that is a separate, independent object, but with the instance variables set so that it is an exact copy of the argument object.

·  Example of a copy constructor for a class with primitive type instance variables - just copy the primitive variables using the assignment operator. In class Date:

public Date(Date original) {
    if(original == null)  {
        System.out.println("Fatal error for this Date.");
        System.exit(0);
    }
    month = original.month;
    day = original.day;
    year = original.year;
}

·  Because of composition, the technique used with Date will not work correctly with class PersonalInfo. The actual copy constructor for the PersonalInfo class is a safe version that creates completely new and independent copies of fullName and birthDate, and therefore, a completely new and independent copy of the original PersonalInfo object.

public PersonalInfo(PersonalInfo original) {
    if(original == null)  {
        System.out.println("Fatal error.");
        System.exit(0);
    }
    //Class Person has to have a copy constructor!
    //Without a copy constructor in Person, the next statement will not work
    fullName = new Person(original.fullName);
    //Class Date has to have a copy constructor!
    //Without a copy constructor in Date, the next statement will not work
    birthDate = new Date(original.birthDate);
    //personId is a primitive type --> use assignment
    personID = original.personID;
}

·  NOTE In order to define a correct copy constructor for a class that uses composition, copy constructors must already be defined for the instance variables' classes (e.g. Date, Person)

·  Arrays and Composition: Just as a class type can be used as an instance variable, arrays can also be used as instance variables. We can define an array with a primitive base type

private double[ ] temps = new double[MAX];

 or an array with a class base type:

private Date[ ] someDates = new Date[MAX];

·  Example:

public double[ ] getTemps( ) {
    return temps; //what is returned here???
}

·  ANSWER: The method will return a reference to the array temps --> should return a reference to a deep copy of the private array object.

·  Corrected version:

public double[ ] copyTemps( ){
    double[ ] copy = new double[temps.length];
    for(int i = 0; i < temps.length; i++)
        copy[i] = temps[i];
    return copy;
}

·  If a private instance variable is an array that has a class as its base type (an array of objects), then you must make copies of each class object in the array, as in the next Example:

public Date[ ] copyDates( ) {
    Date[ ] copy = new Date[someDates.length];
    for(int i = 0; i < someDates.length; i++)
        copy[i] = new Date(someDates[i]);
    return copy;
}


Programming Samples:

Interface: Figure. Classes: Circle, Rectangle, Triangle

// A general interface for all Figure classes.
public interface Figure {
    public double getArea();
    public double getPerimeter();
    public int getSideCount();
    public boolean equals(Object obj);
    public String toString();
}

//Circle class, implementing Figure
//A Circle object represents a pair of (x, y) coordinates
//for the center of the circle, and its radius.
public class Circle implements Figure {
    private int x;
    private int y;
    private double radius;

    //Default constructor
    public Circle() {
        x = 0;
        y = 0;
        radius = 1;
    }

    // Alternate constructor
    public Circle(int x1, int y1) {
        x = x1;
        y = y1;
        radius = 0;
    }

    // Alternate constructor
    public Circle(int x1, int y1, double r) {
        x = x1;
        y = y1;
        radius = r;
    }

    //set center position
    public void setCoord(int x1, int y1) {
        x = x1 >= 0? x1: 0;
        y = y1 >= 0? y1: 0;
    }

    //set radius
    public void setRadius(double r) {
        radius = r;
    }

    // Calculate perimeter
    public double getPerimeter() {
        return 2 * radius * Math.PI;
    }

    // Calculate area
    public double getArea() {
        return Math.pow(radius, 2) * Math.PI;
    }

    // Returns the number of sides a circle has (0).
    public int getSideCount() {
        return 0;
    }

    // Accessor methods
    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public double getRadius() {
        return radius;
    }

    // Method toString
    public String toString() {
        return "center at (" + x + ", " + y + "), radius = " + radius;
    }

    public void print() {
        System.out.print("The center is at (" + x + ", " + y + ")");
        System.out.println(". The radius is " + radius);
    }

    // Compare for equality
    public boolean equals(Object obj) {
        if (obj instanceof Circle) {
            Circle otherCircle = (Circle) obj;
            return (this.x == otherCircle.x && this.y == otherCircle.y && this.radius == otherCircle.radius);
        }
        else   // not a Circle object
            return false;
    }
 

    // Shift the location of this circle by the given amount.
    public void translate(int dx, int dy) {
        if (x + dx < 0 || y + dy < 0) {
            throw new IllegalArgumentException();
        }
        x += dx;
        y += dy;
    }

    //Copy otherCircle into this circle
    public void copy(Circle otherCircle) {
        x = otherCircle.x;
        y = otherCircle.y;
        radius = otherCircle.radius;
    }

    //Make a copy of this circle
    public Circle getCopy() {
        Circle cop =  new Circle(x, y, radius);
        return cop;
    }
}

//Rectangle Class, implementing Figure
public class Rectangle implements Figure {
    private double length;
    private double width;

    //default constructor
    public Rectangle()  {
        length = 0;
        width = 0;
    }

    //alternate constructor
    public Rectangle(double l, double w) {
        length = (l >= 0)? l: 0;
        width = (w >= 0)? w: 0;
        //set(l, w);
    }

    public void set(double l, double w)  {
        length = (l >= 0)? l: 0;
        width = (w >= 0)? w: 0;
    }

    public double getLength() {
        return length;
    }

    public double getWidth() {
        return width;
    }

    public double getArea() {
        return length * width;
    }

    public double getPerimeter() {
        return 2 * (length + width);
    }

    // Returns the number of sides a rectangle has (4).
    public int getSideCount() {
        return 4;
    }

    public void print() {
        System.out.print("Length = "  + length + "; Width = " + width);
    }

    public String toString() {
        return "Length = "  + length + "; Width = " + width;
    }

    public boolean equals(Object obj) {
        if (obj instanceof Rectangle) {
            Rectangle otherRectangle = (Rectangle) obj;
            return length == otherRectangle.length && width == otherRectangle.width;
        }
        else   // not a Rectangle object
            return false;
    }
}

//Triangle Class, implementing Figure.
public class Triangle implements Figure {
    private double a;
    private double b;
    private double c;

    public Triangle() {
        a = 0;
        b = 0;
        c = 0;
    }
    // Constructs a new Triangle given side lengths.
    public Triangle(double a, double b, double c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }

    // Returns this triangle's area using Heron's formula.
    public double getArea() {
        double s = (a + b + c) / 2.0;
        return Math.sqrt(s * (s - a) * (s - b) * (s - c));
    }

    // Returns the perimeter of this triangle.
    public double getPerimeter() {
        return a + b + c;
    }

    // Returns the number of sides a triangle has (3).
    public int getSideCount() {
        return 3;
    }

    public void print() {
        System.out.print("side a = "  + a + "; side b = " + b + "; side c = " + c);
    }

    public String toString() {
        return "side a = "  + a + "; side b = " + b + "; side c = " + c;
    }

    public boolean equals(Object obj) {
      if (obj instanceof Triangle) {
          Triangle otherTriangle = (Triangle) obj;
          return a == otherTriangle.a && b == otherTriangle.b  && c == otherTriangle.c;
      }
      else   // not a Triangle object
          return false;
    }
}

// Client for classes implementing Figure.
public class ClientFigures {
    public static final int MAX = 3;
    public static void main(String[] args) {
        Figure[] fig = new Figure[MAX];
        fig[0] = new Rectangle(37, 20);
        fig[1] = new Triangle(37, 20, 30);
        fig[2] = new Circle(0, 0, 37);
        int sides;

        for (int i = 0; i < fig.length; i++) {
            sides = fig[i].getSideCount();
            switch(sides) {
                case 0:
                    System.out.print("\nThis is a circle. ");
                    break;
                case 3:
                    System.out.print("\nThis is a triangle. ");
                    break;
                case 4:
                    System.out.print("\nThis is a rectangle. ");
            }
            System.out.printf("Area = %.2f", fig[i].getArea());
            System.out.printf(". Perimeter = %.2f\n", fig[i].getPerimeter());
            System.out.println("Output calling the method printInfo - polymorphism at work!");
            printInfo(fig[i]);
        }
    }

    //Any object that implements the interface may be passed as parameter to this method.
    public static void printInfo(Figure f) {
        System.out.println("For this figure: " + f);
        System.out.printf("Area = %.2f", f.getArea());
        System.out.printf("; Perimeter = %.2f\n", f.getPerimeter());
    }
}
OUTPUT:
This is a rectangle. Area = 740.00. Perimeter = 114.00
Output calling the method printInfo - polymorphism at work!
For this figure: Length = 37.0; Width = 20.0
Area = 740.00; Perimeter = 114.00

This is a triangle. Area = 299.50. Perimeter = 87.00
Output calling the method printInfo - polymorphism at work!
For this figure: side a = 37.0; side b = 20.0; side c = 30.0
Area = 299.50; Perimeter = 87.00

This is a circle. Area = 4300.84. Perimeter = 232.48
Output calling the method printInfo - polymorphism at work!
For this figure: center at (0, 0), radius = 37.0
Area = 4300.84; Perimeter = 232.48

Composition: Classes: Person, Date, PersonalInfo

//Class Person:
public class Person {
    private String firstName;
    private String lastName;

    //Default constructor
    public Person() {
        firstName = " ";
        lastName = " ";
    }

    //Alternate Constructor
    public Person(String first, String last) {
        setName(first, last);
    }

    //Copy constructor
    public Person(Person original) {
        if(original == null)  {
            System.out.println("Fatal error for this Person.");
            System.exit(0);
        }
        firstName = original.firstName;
        lastName = original.lastName;
    }

    public String toString(){
        return (lastName + ", " + firstName);
    }

    public void printLastFirst() {
        System.out.print(lastName + ", " + firstName);
    }

    public void printFirstLast() {
        System.out.print(firstName + " " + lastName);
    }

    public void setName(String first, String last) {
        firstName = first;
        lastName = last;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public boolean equals(Object obj) {
      if (obj instanceof Person) {
          Person otherPerson = (Person) obj;
          return lastName.equals(otherPerson.lastName) && firstName.equals(otherPerson.firstName);
      }
      else   // not a Person object
          return false;
    }
}

//Class Date:
public class Date {
    private int month;
    private int day;
    private int year;

    //Default constructor
    public Date() {
        month = 0;
        day = 0;
        year = 1900;
    }

    //Alternate constructor
    public Date(int m, int d, int y) {
        month = (m >=1 && m <= 12)? m: 0;
        day = (d >=1 && d <= 31)? d: 0;
        year = (y >=1900 && y <= 2020)? y: 0;
        //setDate(m, d, y);
    }

    //Copy constructor
    public Date(Date original) {
        if(original == null)  {
            System.out.println("Fatal error for this Date.");
            System.exit(0);
        }
        month = original.month;
        day = original.day;
        year = original.year;
    }

    public void setDate(int m, int d, int y) {
        month = (m >=1 && m <= 12)? m: 0;
        day = (d >=1 && d <= 31)? d: 0;
        year = (y >=1900 && y <= 2020)? y: 0;
    }

    public int getMonth() {
        return month;
    }

    public int getDay() {
        return day;
    }

    public int getYear() {
        return year;
    }

    public String toString() {
        return (month + "-" + day + "-" + year);
    }

    public boolean equals(Object obj) {
      if (obj instanceof Date) {
          Date otherDate = (Date) obj;
          return month == otherDate.month && day == otherDate.day && year == otherDate.year;
      }
      else   // not a Date object
          return false;
    }
}

//Class PersonalInfo (composition)
public class PersonalInfo {
    private Person fullName; //(class) Person type
    private Date birthDate//(class) Date type
    private int personID;

    //Default constructor
    public PersonalInfo() {
        fullName = new Person();
        birthDate = new Date();
        personID = 0;
    }

    //Alternate constructor
    public PersonalInfo(String first, String last, int m, int d, int y, int id) {
        fullName = new Person(first, last);
        birthDate = new Date(m, d, y);
        personID = id;
    }

    //Copy constructor
    public PersonalInfo(PersonalInfo original) {
        if(original == null)  {
            System.out.println("Fatal error.");
            System.exit(0);
        }
        //Person has to have a copy constructor!
        fullName = new Person(original.fullName);
        //Date has to have a copy constructor!
        birthDate = new Date(original.birthDate);
        personID = original.personID;
    }

    public void setPersonalInfo(String first, String last, int m, int d, int y, int id) {
        fullName.setName(first, last); //method setName in class Person
        birthDate.setDate(m, d, y);    //method setDate in class Date
        personID = id;
    }

    public Person getfullName( ){
        return new Person(fullName); //Person has to have a copy constructor!
    }

    public Date getBirthDate( ){
        return new Date(birthDate); //Date has to have a copy constructor!
    }

    public int getPersonID( ){
        return personID;
    }

    public String toString() {
    //return ("Name: " + fullName.toString() + "\nDate of birth: " + birthDate.toString() + "\nPersonal ID: " + personID);
       //Same as:
       return ("Name: " + fullName + "\nDate of birth: " + birthDate + "\nPersonal ID: " + personID);
    }

    public boolean equals(Object obj) {
        if (obj instanceof PersonalInfo) {
            PersonalInfo otherPerson = (PersonalInfo) obj;
            return  fullName.equals(otherPerson.fullName) && birthDate.equals(otherPerson.birthDate) && personID == otherPerson.personID;
        }
        else   // not a PersonalInfo object
            return false;
    }
     /*Another version for equals:
    public boolean equals(PersonalInfo otherPerson) {
        if (otherPerson == null)
            return false;
        else
            return  fullName.equals(otherPerson.fullName) && birthDate.equals(otherPerson.birthDate) && personID == otherPerson.personID;
    }*/

    public boolean sameName(Object obj) {
        if (obj instanceof PersonalInfo) {
            PersonalInfo otherPerson = (PersonalInfo) obj;
            return  fullName.equals(otherPerson.fullName);//equals from Person
        }
        else   // not a PersonalInfo object
          return false;
    }

    public boolean sameBirthDate(Object obj) {
        if (obj instanceof PersonalInfo) {
            PersonalInfo otherPerson = (PersonalInfo) obj;
            return  birthDate.equals(otherPerson.birthDate);//equals from Date
        }
        else   // not a PersonalInfo object
          return false;
    }

    public boolean sameID(Object obj) {
        if (obj instanceof PersonalInfo) {
            PersonalInfo otherPerson = (PersonalInfo) obj;
            return  personID == otherPerson.personID;
        }
        else   // not a PersonalInfo object
          return false;
    }
}

//Client for PersonalInfo class
public class ClientComposition {
    public static void main(String[] arg)   {
        PersonalInfo newStudent = new PersonalInfo("James", "Smith", 2, 20, 1988, 12345678);
        System.out.println(newStudent);
        PersonalInfo otherStudent = new PersonalInfo("Jim", "Smith", 2, 20, 1988, 12345679);
         System.out.println(otherStudent);
        if(newStudent.equals(otherStudent))
            System.out.println("Same personal info.");
        else
            System.out.println("NOT same personal info.");
        if(newStudent.sameName(otherStudent))
            System.out.println("Same name for both students.");
        else
            System.out.println("Students don't have the same name.");
        if(newStudent.sameBirthDate(otherStudent))
            System.out.println("Same birthdate for both students.");
        else
            System.out.println("Students don't have the same birthdate.");
        if(newStudent.sameID(otherStudent))
            System.out.println("Something is wrong! Same ID for both students.");
        else
            System.out.println("Students don't have the same ID.");

    }
}
OUTPUT:
Name: Smith, James
Date of birth: 2-20-1988
Personal ID: 12345678
Name: Smith, Jim
Date of birth: 2-20-1988
Personal ID: 12345679
NOT same personal info.
Students don't have the same name.
Same birthdate for both students.
Students don't have the same ID.


Key Terms
Abstract class:  A class that is declared with the reserved word
abstract.  You cannot instantiate an object of an abstract class.
Abstract method: A method that has only the heading with no body.
Composition: The use of classes as instance variables in the definition of another class.
Dynamic binding: The method that gets executed is determined at execution time not compile time; same as run-time binding.
Interface: A class that contains only abstract methods and/or named constants. This is how Java implements multiple inheritance, which is not true multiple inheritance.
Overriding: Declaring a method in a subclass with the same signature as a method in its superclass.
Overloading: Declaring a method with the same name, different parameters.
Polymorphism: Assigning multiple meanings to the same method name. The ability for the same code to be used with several different types of objects and behave differently depending on the type of object used.
Run-time binding: The method that gets executed is determined at execution time not compile time; same as dynamic binding.


Additional Resources:
1. (Sun) API specification for version 6 of the Java™ Platform, Standard Edition (Class Object): http://java.sun.com/javase/6/docs/api/
2. (Sun) The Java Tutorials, Interfaces: http://java.sun.com/docs/books/tutorial/java/IandI/createinterface.html


References:
[1] Java Programming: From Problem Analysis to Program Design, by D.S. Malik, Thomson Course Technology, 2008
[2] Building Java Programs
: A Back to Basics Approach, by Stuart Reges, and Marty Stepp, Addison Wesley, 2008.