Kinesia Online Course
Object Oriented Programming
Kinesia LLC, 2003
    1. A Little Taste of Java
    2. Abstraction and Modeling
    3. Objects and Classes
    4. Object Interactions
    5. UML and Relationships between Objects
    6. Collection of Objects
    7. Some Final Concepts
    8. Object Modeling and Use Cases
    9. Modeling of Dynamic Behavaior and Sequence Diagrams
    10. A Deeper Look at Java
    11. Java Layout
    12. Java Events
    
    The history of free man is never written by chance
    but by choice -- their choice.
    					Dwight D. Eisenhower
    
    

    Some Final Concepts

    1. Polymorphism

      Webster:

      the quality or state of being able to assume different forms

      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:

    2. Bad abstraction and non-optimal use of space because all types have to be placed in one format.

    3. Non-standardized type identification fields. You have to memorize what value identifies what type.
    4. OO approach: Use polymorphism:

    5. Polymorphism is ability of objects to act depending on their run-time type.

    6. Works hand-in-hand with inheritance and overriding. Provides a mechanism to deal with awareness of the run-time (not the compile-time) class of the particular object, regardless of where it's placed in compile-time declared class.

    7. Very important for effective use of inheritance. Frequently, it is used to "persuade" methods of parent class to use redefined methods of the child class.

    8. If we have a number of objects with the same interface, but different behaviour, we can create one base class that will contain all common methods. These methods should be virtual. After that, from that base class, we'll inherit all needed methods. Then, we'll declare a collection type whose elements are declared as instances of the base class, but in the run-time, instances of child classes will be placed instead.

    9. Virtual methods are methods aware of polymorphism.

    10. Abstract methods are declared only to be overridden in a child class. No implementation of it exists. In C++, this is called pure virtual member function.

    11. Class that contains at least one abstract method is an abstract class.

    12. Class with all methods abstract and without fields is interface. Such class is merely declaration of interface without practical sense until implemented by child classes.
    13. Binding

    14. Static binding -- compiler and linker make machine code that contains the actual address of the method.

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

      • Every instance of the class that contains (or inherits) at least one virtual method has special hidden pointer in it's physical beginning. That pointer contains an address of special data structure called "virtual calls table" (or just "virtual table").

      • Virtual calls table is array of pointers to all virtual methods accessible from object.

      • When we inherit the class, a new virtual table is generated for a child class we made. In that new version of virtual table, order of pointers to methods remains the same. Pointer to method that is overridden in the child is changed and points to the new version of the method. If the child introduces new methods, corresponding pointers are added to the end of the virtual table, without disturbing order of previously existing pointers. Hidden pointer of every instance of the child class will point to this new virtual table.

      • Machine code that needs to make a call to the virtual method doesn't contain any pointer to the method, but index of the method pointer in the virtual table. When that code executes, it will read a hidden pointer to virtual table from the object itself, and by this pointer, have access to the virtual table of actual run-time class of the object. Then it will read pointer from correct index of virtual table, and jump execution to it. So the method of actual run-time class is called.
    15. Polymorphism Varieties and Examples

      Polymorphism can be classified to

                                       |-- coercion
                       |-- ad hoc    --|
                                       |-- overloading
        polymorphism --|
                                       |-- parametric
                       |-- universal --|
                                       |-- inclusion
      

    16. Coercion: a single abstraction serves several types through implicit type conversion
      Examples:
        	 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 );
        	

    17. Overloading: a single identifier denotes several abstractions
      Overloading permits the use of the same operator or method name to denote multiple, distinct program meanings.

      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.

      Parametric: an abstraction operates uniformly across different types.
      For example, a List abstraction, representing a list of homogeneous objects, could be provided as a generic module. You would reuse the abstraction by specifying the types of objects contained in the list. Since the parameterized type can be any user-defined data type, there are a potentially infinite number of uses for the generic abstraction, making this arguably the most powerful type of polymorphism.

    18. Inclusion: an abstraction operates through an inclusion relation.
      For many object-oriented languages, including Java, the inclusion relation is a subtype ( inheritance ) relation. So in Java, inclusion polymorphism is subtype polymorphism.
    19. 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:

      Derived2 derived2 = new Derived2();


      Figure a

      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

      Base base = derived2;


      Figure b

      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()"
      

    20. Interface to an Object

      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.

    21. Static Attributes

      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:

    22. each method would be duplicating the same information ( totalStudents ); wasting memory

    23. In the example, each new Student is created, we need to call incrementEnrollment() method for all other existing Student objects to reflect the change; error prone and cumbersome
    24. 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;
        }
        ....
      }
      

      Student Object
      name: Arnold
      ssn: 9999
      (totalStudents)
      Student Class
      name: ???
      ssn: ???
      totalStudents: 3
      Student Object
      name: George
      ssn: 1119
      (totalStudents)
             
         
      Student Object
      name: Jerry
      ssn: 9111
      (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;
      

    25. Static Methods

      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

    26. instance variable referrs to 'normal' ( non-static ) variable

    27. class variable refferrs to static variable