Object-Oriented Programming

CSCI-UA.0470-001

NYU, Undergraduate Division, Computer Science Course - Fall 2013

OOP Class Notes for 09/24/13

Overview of Inheritance, Method Overriding and Virtual Method Dispatch

Overall, inheritance enables modularity (separate classes), code reuse (inhertiance itself), and selective extensibility (method overriding).

Why Use Inheritance?

  • Bad ways to create a Color Point
    • Modifying the existing code for the point class
      • This means all instances of a Point must have a Color
      • Less flexible and wastes space if we don't need Color in an implementation
      • Doing this over and over will make the Point class very complex, making it harder to read and more likely to introduce bugs with each modification
    • Copy the entire code and create a new class with it that contains both point and color as fields
      • Extra work to rewrite all of the methods of Point to correspond to a Color Point
      • Writing the same code that was written before
      • Might copy bugs, which will take twice as long to squash
      • Note : Copying and pasting isn't necessarily bad, especially while getting code up and running, just use it as a tool to work toward good code, not as a final solution.
  • Better way to do it - Inheritance
    • Color Point (the subclass) inherits from Point (the superclass)
    • Subclass inherits all of the superclass's behavior and structures, i.e. methods and data
    • This is called the "is a" relationship - e.g. every Color Point is a Point, but not vice versa
      • The subclass is guaranteed to have all of the properties of the superclass in addition to newer, more specific properties
    • This concept is called (subtype) polymorphism. Use inheritance to save coding effort.
  • For the following sections, refer to Point.java from the 2nd lecture for the source code for the Java Point and the code from today's lecture for the source code for the C++ Point, Color, ColorPoint and main

Inheritance in Java

  • java.lang.Object is a static supertype you can use anywhere
    • It is the root of the class hierarchy in Java
    • It is implicit that every class inherits it. There is no need to write extends Object.
    • Every class in Java inherits and implements Object's methods implicitly (there is no data)
      • For the translator project we only have to implement the following:
        
        String toString()
        
        int hashCode()
        
        boolean equals(Object o)
        
        Class getClass() 
  • The is a relationship describes the relationship between classes and superclasses - every Point is an Object
    • Object is more generic than Point
    • You can use a Point anywhere an Object is specified but not vice versa
  • Take a look at how we can use a Point
  • 
    public static void main(String[] args) {
    
    Object o = new Point(0, 1, 2, 3);
    
    
    
    String str = o.toString();
    
    String str = new Object().toString();
    
    
    
    int dist1 = o.getDistanceFrom(Point.ORIGIN);
    
    int dist2 = ((Point) o).getDistanceFrom(Point.ORIGIN);
    
    }
  • Is Object o = p1; legal?
    • Everywhere there is an Object a Point may appear
    • Statically, assigning a variable of type Point but in practicality it is different
  • Is o.toString() legal java code?
    • Yes! o is statically declared to be an Object
    • Compiler knows this must be an Object and have all of Object's methods
    • This will use Point's toString() method because Point overwrites the toString() method in Object, i.e. it is the most specific version of toString() available
  • Is o.getDistanceFrom(Point.ORIGIN) legal?
    • No, there is no way to know you are always referencing a Point - you can't use something more generic than required
      • You can solve this by explicitly casting a type like the last line in the example above: (Point) o
    • A method accepting an Object argument may be called with any Object, including a Point
  • If you just called new Object().toString() you would just get the the name of the object's Java class and the object's hash code ("java.lang.Object@38503429")
  • Use the superclass to declare the major methods, i.e. to define your object hierarchy's common functionality
  • Use subclasses to specialize the methods to your liking
  • Java syntax for inheritance is class Point extends Object

Inheritance in C++

  • C++ has no class hierarchy root like Java, i.e. there is no equivalent to Object in C++
    • Inheritance is defined explicitly
    • C++ distinguishes between initializing a value and assigning one. Assignments may make copies initializers don't.
    • The is a relationship - every ColorPoint is a Point
  • Take a look at Color.h, which defines a color object
    class Color {
    
    unsigned char r, g, b;
    
    ...
    
    Color(unsigned char r, unsigned char g, unsigned char b)
    : r(r), g(g), b(b) {} unsigned char red() const {
    return r;
    } ... };
    • : r(r), g(g), b(b) is an intializer list
      • The first r in r(r) refers to the field r in the scope of the class.
      • The second r in r(r) refers to the value r in the scope of the constructor.
      • These values are initialized in the order in which they are declared within the class.
        • In Color, the initialization order is r, g, b because they are declared unsigned char r, g, b, not because of the order they appear in the initializer list.
      • Only with the C++ standard C++11 do initializer lists support initialization of arrays. If your compiler does not support C++11 then you must initialize arrays in the body of the constructor.
      • Using initializer lists is considered good style in C++.
      • We specify unsigned char because whether char is signed/unsigned is implementation specific; we have decided to be explicit about it in our code.
    • The getters for the RGB values are inlined into this header file as well.
      • Question : What is right to inline?
        • Trivial actions like returning
        • Initialization of variables
        • Some basic operations
    • They are const because we are not modifying the actual data
  • Now take a look at Point.h, where we define a Point and a ColorPoint
    • A ColorPoint can appear everywhere a Point can appear.
    • Every ColorPoint is a Point, so we will want to re-write (i.e. override) the method toString()
      • We use the keyword virtual in front of the method declaration in the Point class to let the compiler know we want to be able to automatically override a method
        virtual string toString() const;
      • Once a method is declared virtual, it is automatically a virtual method in all of its subclasses and does not need to be specified as virtual again in order to be overridden
    • C++ syntax for inheritance is class ColorPoint : public Point (the superclass must be public)

    Static Typing, Dynamic Typing and Virtual Method Dispatch

    • For variables p that point to an object, we distinguish between p static and dynamic types.
      • Static type: the type of p known to the compiler at compile time, i.e., from the declaration of p.
      • Dynamic type: the actual type of p at runtime determined by the object to which p points.
        • Without inheritance, static and dynamic types are the same
        • With inheritance, the dynamic type is the type of the object the variable was last changed to.
      • Virtual Method Dispatch
        • In C++, methods are called based on the dynamic type only if they are virtual; otherwise they are called based on the static type
        • All Java methods are virtual, except private methods, because you can't see them, and static methods, because they don't have an associated instance
        • In other words, all Java public and protected instance methods are virtual; you can override them in a subclass to specialize the behavior of a static type
        • Virtual methods in Java and C++ look for the most specific implementation of a method, i.e. the one furthest down the inheritance chain
        • Translating these types of Java methods to C++ code that does not use virtual methods is the CORE OF THE PROJECT.
        • Virtual method dispatch only works in C++ with pointers and references
      • C++ pointers and virtual method dispatch
        Point* p = new ColorPoint(Color::WHITE, 0, 1, 2, 3);
        
        p->toString();
        
        ColorPoint* cp = new ColorPoint(Color::RED, 0, 1, 2, 3);
        
        cp->toString();
        
        Point::ORIGIN.getDistanceFrom(*cp)
        • The new operator allocates memory for the object on the heap (like in Java) rather than inline (which Java cannot do)
        • The dynamic type of both p and cp is a ColorPoint* (a pointer to a ColorPoint)
        • The static type for p is a Point* and the static type for cp is a ColorPoint*
        • Since cp is a ColorPoint*, cp->toString() will call cp's toString() no matter what
        • p will also call ColorPoint's toString() based on our implementation (the method is declared as virtual and p's dynamic type is ColorPoint*)
          • If we did not make it a virtual method p->toString() would call Point's toString() based on its static type
        • If you look at Point::ORIGIN.getDistanceFrom(*cp) you will see that, like Java, you can use a ColorPoint where a Point is expected because a Point is more generic
        • Remember, for C++, you have to use arrow notation for pointers to objects, i.e p->toString()