Lecture 23, OOP

Reading

Slide handouts 11.

Course book (the following sections and their respective subsections)

Study Guide

This lecture looks at the implications of using the "extends" clause in a class definition. Or in short; what does it mean to specify class B extends A. This operation is generally known as "inheritance".

Generally there are many definitions of what inheritance is.. almost one for each programming language. Although we shall only focus on Java, the lecture takes up many of the fundamental issues which are tied to inheritance. The lecture may seem fairly theoretical, but is in fact only covering parts of what a programmer should know about the programming language he uses.

It is assumed that you already know the basic inheritance stuff such as implementing an interface, abstract classes and the final modifier as they will not take up much time, if any.

The main issues the lecture covers are

Exercises

Some of the exercises covers things we have not been talking about during the lecture. This is no coincidence, as they fill out the empty spaces left from the lecture. The exercises continues the systematic approach from the lecture, and through the course in general; to exhaustivly examine all cases of a given topic.

1. Polymorphic references.

Given the code
class A {                           class C {
	void foo(){...}                     void pip(){...}
}                                   }

class B extends A {
	void bar(){...}
}
  1. Which of these constructs are allowed?
    A a = new A()
    A a = new B()
    A a = new C()
    B b = new A()
    B b = new B()
    
    Explain in your own words what goes wrong with the faulty constructs and why the correct ones are correct.

2. Shadowing of fields.

In the lecture we did not touch upon fields and inheritance. In some languages it is possible for a sub class to contain fields which have identical names with fields of any of the class' super classes. To find out if this is possible in Java try to compile the following program, which in fact does compile nicely. In Java terminology the x in B shadows x in A.
class A {
	int x = 8;
	void print() {
		System.out.println(this.x);
	}
}

class B extends A {
	String x = "hello world";
}
Many have learned to construct getter and setter methods for fields in order to restrict which values are assigned to the fields. In this exercise we shall examine the getter/setter idiom when used on fields that are shadowed.
  1. What is printed on the screen when calling print() on A and B instances?
  2. Create a getter method in both A and B for x (named getX()). The method in A should return an Integer whereas in B it should return a String. Hint: Remember that a method can return an object of a subtype of the return type (due to polymorphism).
  3. Why do we have to use a misleading return type for getX()? (misleading in that the type is neither Integer nor String).
  4. Change the print method in A to use the getter method rather than accessing the field directly. What is now printed on the screen when calling print() on A and B instances? If the result is different, why is that so?
  5. Based on your experiences, make a short conclusion on using getter/setter methods and shadow fields.

3. Practice with inheritance.

Consider the following two classes, which are not too well designed. This exercise is about making a better design. The two classes represent a point on a screen, and a line on a screen. The point has an x coordinate, and a y coordinate, and it can be moved. The Line as a starting point (which its inherit from Point), and an end point which it implements it self with its own x and y.
class Point {
   private int x;
   private int y;
   public Point(int x, int y){
      this.x = x;
      this.y = y;
   }
   public void move(int dx, int dy){
      x+=dx;
      y+=dy;
   }
}

class Line extends Point {
   private int x;
   private int y;
   public Line(int x1,int y1, int x2, int y2){
      super(x1,x2);
      this.x = x2;
      this.y = y2;
   }
   public void move(int dx, int dy){
      super.move(dx,dy);
      x += dx;
      y += dy;
   }
}

class PointAndLineUser {
   Point p;
   Line l;

   PointAndLineUser(){
      p = new Point(3,4);
      l = new Line( 10, 20, 30, 40 );
   }

   void MoveAll(){
      p.move(7,1);
      l.move(3,6);
   }
}
  1. We shall examine the call "l.move(3,6)". Draw the method calls of the move method, and its call to the super method. The state of the involved objects should depict the situation just after the assignment in which one of the x fields in l has gotten the value 13, but before any y field has gotten the value 26. Pay special attention to the type of the "this" field in the method calls.
  2. Really, the x and y in class Line represents a point. Change the class Line so the it becomes:
    class Line extends Point{
       private Point end;
       ...
    }
    
    Rewrite the constructor and move method on Line so that the class does the same as before.
  3. In 2) an end point was introduced. But no explicit start point, we just inherit that. As an alternative, we could have implemented it as:
    class Line {
       private Point start;
       private Point end;
       ...
    }
    
    Rewrite the constructor and move method so that the class does the same as before.
  4. One of the advantages of the first two solutions was that we could have a reference r (of a more specific type than Object) which could refer to both points and lines. a) Why was that possible? b) what change should one do to solution 3) in order to achieve a similar effect?

4. Interfaces == multiple inheritance substitution.

Through time many languages has had support for 'Multiple inheritance'. But often people had difficulties understanding the code, especially if virtual methods had the same signature in many super classes. When creating Java they simplified the language by removing the concept of multiple inheritance. Instead interfaces were introduced as a more readable substitution. With interfaces one can only inherit signatures rather than code, but are all problems in the world solved now?

One of the most famous problems regarding multiple inheritance is that if class C extends A, B {...} where both A and B contain the method void foo() what happens when calling foo on a C object? Are both foo's from A and B called? or only one of them? And what does foo really mean in C now that it magically is both. Here we shall try the same situation to see if the problem still persist in Java.

Consider the following interfaces..

interface Cowboy {
	void draw();                    // draw the gun - get ready to pump some iron
	void drink();                   // enjoy some beverage
	void burp();                    // communicate with other people in the bar (or where ever)
}

interface Drawable {
	void draw(int x, int y);        // draw the drawable item on the screen
}
  1. Write a person class which implement the two interfaces (the implementation should be very simple, eg. just a print() in each method)
  2. Can it be handled if the two interfaces draw method has the same signature (same name, return type and arguments).. eg. both are void and take zero arguments ? Can you somehow decide if you want the person to wave his gun around or if he should be drawn on some canvas? Change the code acordingly and play around with it.
  3. What happens if you change the return type of draw in one of the interfaces to be int and String in the other interface (but each still take zero arguments)?
  4. In conclusion: How well do you think Java solves the problem of implementing several interfaces where methods accidently have the same name?

5. Type casts.

Given
class A {
	void foo(){ System.out.print("a"); }
}

class B extends A {
	void foo(){ System.out.print("b"); }
}
  1. what is the result of the following code?
    1. A a = new B(); a.foo();
    2. B b = new B(); b.foo();
    3. ((A)b).foo()
  2. What can you say about accessing methods (using casts and references of different kinds) of class A overwritten in class, when you have an object of type B?
  3. Can you by "cheating" (inserting code into B) get access to the foo method of class A when you have an object of type B?

6. interface based development (Optional).

  1. Which of the below two pieces of code is significantly better than the other? Is that piece also the one with less code? (less code is also important, as less code typically means less maintenance and bugs :-)
code Acode B
interface MyCollection {
	public void add(Object o);
	public Object get(int i);
}

class List implements MyCollection {
	public void add(..){..}
	public Object get(..){..}
}

class Client {
	MyCollection c = new List()
	..
}
class List {
	public void add(..){..}
	public Object get(..){..}
}

class Client {
	List c = new List()
	..
}

7. Static methods and the notion of virtual. (Optional and tricky)

  1. We have seen that instance methods are virtual in Java. If You are not convinced of this "magic", you should try out a small example showing it. If you cannot make an example program showing this, look in the slides for such a program.
  2. We have not touched upon static methods in the lecture. Show the dispatching rules of static methods in Java (in other words; are static methods virtual?). Make a test program showing this. Hint: the solution looks a bit different since we cannot use the 'super' construct (why?). Instead we utilize that references are polymorphic. Also note that since we cannot use 'super' it sufices to have just one method in each class.