|
The history of free man is never written by chance but by choice -- their choice. Dwight D. Eisenhower
Some Final Concepts
Webster:
Suppose we need a bunch of different objects that are used in the same way like geometric shapes. We want to let user fill some data structure with squares, triangles, circles,... and call Draw() for all of them, without further complications (such as explicit type checking).
Procedural languages like C use structures or unions that contain data-type identification field. Weaknesses of this approach:
OO approach: Use polymorphism:
Binding

Dynamic binding -- used by virtual methods; the methods do not link to machine codes until run time:

Polymorphism can be classified to
|-- coercion
|-- ad hoc --|
|-- overloading
polymorphism --|
|-- parametric
|-- universal --|
|-- inclusion
2.0 + 2.0; //compiler implicitly converts the second operand to double and //uses the operator defined for two double operands. 2.0 + 2; //The compiler coerces the double operand to a String, and the plus //operator performs string concatenation. 2.0 + "2"; //Suppose class Derived extends class Base, and class C //has a method with signature m(Base). //For the method invocation in the code below, the compiler implicitly //converts the derived reference variable, which has type Derived, to //the Base type prescribed by the method signature. C c = new C(); Derived derived = new Derived(); c.m( derived );
Both coercion and overloading are classified as ad hoc because each provides polymorphic behavior only in a limited sense. Though they fall under a broad definition of polymorphism, these varieties are primarily developer conveniences.
Examples:

/* Base.java */
public class Base
{
public String m1()
{
return "Base.m1()";
}
public String m2( String s )
{
return "Base.m2( " + s + " )";
}
}
|
/* IType.java */
interface IType
{
String m2( String s );
String m3();
}
|
/* Derived.java */
public class Derived
extends Base
implements IType
{
public String m1()
{
return "Derived.m1()";
}
public String m3()
{
return "Derived.m3()";
}
}
|
/* Derived2.java */
public class Derived2
extends Derived
{
public String m2( String s )
{
return "Derived2.m2( " + s + " )";
}
public String m4()
{
return "Derived2.m4()";
}
}
|
/* Separate.java */
public class Separate
implements IType
{
public String m1()
{
return "Separate.m1()";
}
public String m2( String s )
{
return "Separate.m2( " + s + " )";
}
public String m3()
{
return "Separate.m3()";
}
}
|
Using these type declarations and class definitions, the following figure depicts a conceptual view of the Java statement:
The top panel in the Figure depicts the Derived2 reference as a set of portholes, through which the underlying Derived2 object can be viewed.
Consider now
There is no change to the underlying Derived2 object or any of the operation mappings, though methods m3() and m4() are no longer accessible through the Base reference. Calling m1() or m2(String) using either variable derived2 or base results in execution of the same implementation code:
String tmp; // Derived2 reference (Figure a) tmp = derived2.m1(); // tmp is "Derived.m1()" tmp = derived2.m2( "Hello" ); // tmp is "Derived2.m2( Hello )" // Base reference (Figure b) tmp = base.m1(); // tmp is "Derived.m1()" tmp = base.m2( "Hello" ); // tmp is "Derived2.m2( Hello )" |
However, base does not see m3() and m4():
String tmp; // Derived2 reference (Figure a) tmp = derived2.m3(); // tmp is "Derived.m3()" tmp = derived2.m4(); // tmp is "Derived2.m4()" // Base reference (Figure b) tmp = base.m3(); // Compile-time error tmp = base.m4(); // Compile-time error |
Now, consider a reference variable named ref attaches to an object whose class contains the following method definition:
public String poly1( Base base )
{
return base.m1();
}
|
The following code creates three objects from different classes and passes a reference to each into poly1(Base):
Derived2 derived2 = new Derived2(); Derived derived = new Derived(); Base base = new Base(); String tmp; tmp = ref.poly1( derived2 ); // tmp is "Derived2.m1()" tmp = ref.poly1( derived ); // tmp is "Derived.m1()" tmp = ref.poly1( base ); // tmp is "Base.m1()" |

In terms of type, the interface to an object refers to the widest possible type-oriented view of that object. A super type reference attached to the same object typically narrows the view. The concept of type best captures the spirit of freeing object interactions from the details of object implementation. Rather than refer to the interface of an object, a type-oriented perspective encourages referring to the reference type attached to an object. The reference type dictates the permissible interaction with the object. Think type when you want to know what an object can do, as opposed to how the object implements its responsibilities.
Suppose a reference variable named ref attaches to an object whose class contains the following method definition:
public String poly2( IType iType )
{
return iType.m3();
}
|
To explore polymorphic behavior inside poly2(IType), the following code creates two objects from different classes and passes a reference to each into poly2(IType):
Derived2 derived2 = new Derived2(); Separate separate = new Separate(); String tmp; tmp = ref.poly2( derived2 ); // tmp is "Derived.m3()" tmp = ref.poly2( separate ); // tmp is "Separate.m3()" |

By grouping objects from disparate implementation hierarchies, Java interfaces facilitate polymorphic behavior even in the absence of any shared implementation or overridden methods. As shown in the above Figure, an IType reference polymorphically accesses the m3() methods of the underlying Derived2 and Separate objects.
Suppose we need to frequently accessing some information. This can be obtained from a method of a class like:
class Student {
private int totalStudent;
.....
public int getTotal() {
return totalStudent;
}
public void incrementEnrollment() {
++totalStudents;
}
....
}
|
This would be inefficient, because:
better use static attributes which are 'shared' among all objects belonging to the same class:
class Student {
private static int totalStudent;
.....
public int getTotal() {
return totalStudent;
}
public void incrementEnrollment() {
++totalStudents;
}
....
}
|
|
|
|
|
|
||||||
|
||||||||||
|
static attributes can be private or public; if public, can be accessed via dot notation:
e.g. Student s1 = new Student; System.out.println( s1.totalStudents );or can be accessed as an attribute of the class as a whole
e.g. System.out.println( Student.totalStudents ); Color.green;
static methods can be invoked on the class as a whole
e.g. int x = Student.getTotalStudents(); if getTotalStudents() has been declared as static int x = Integer.parseInt( aString ); double d = Math.random();
Often
|
|