W4: Polymorphism

Please submit your answers to the questions as comments in a W4.md file you’ll be writing in this lecture.

Grading rubric and submission

When you are done, submit your W4.md file to BB.

You will be graded on the following:

Item Points
Answers are correct 100

Questions

0

Define polymorphism in the context of Java and provide one example where it is valuable.

Answer

Polymorphism in Java allows multiple implementation of the same interface. One example will be the Point and CustomPoint classes we implementated in class where we can have a toString() method in both Point and CustomPoint that prints out different information.

1

Consider the following program from the class notes

public class Ex3 {
  public static void main(String[] args) {
    Random   rand = new Random(System.currentTimeMillis());
    Point    v    = new Point(3, 4);
    LabPoint w    = new LabPoint(5, 2, "A");
    String   x    = "I'm a string";
    Scanner  y    = new Scanner(System.in);

    Object u;
    int i = rand.nextInt(4);

    if( i == 0 )
      u = v;
    else if( i == 1 )
      u = w;
    else if( i == 2 )
      u = x;
    else
      u = y;
    System.out.println(u.toString()); //<--
  }
}

Explain how polymorphism makes this program possible.

Answer

Since every class that does not have the extends keyword implicitly extends the Object class, the Object u declaration makes u capable of “morphing” into any of the 4 variables v, w, x, y.

2

What is the output of this program? You should be able to do this without running the program!

class A {
    public String toString(){
        return "A";
    }
}

class B extends A{
    public String toString() {
        return "B";
    }
}

class C extends A {
    public String toString() {
        return super.toString();
    }
}

class D extends C {
    public String toString() {
        return super.toString();
    }
}

public class tmp {
    public static void main(final String args[]) {
        D d = new D();
        System.out.println(d.toString());
    }
}

Answer

The output is A because class D extends class C and class C extends class A where both classes C, D have their toString() function return super.toString() and the toString() method of class A returns a single character A.

3

What is the output of this program? You should be able to do this without running the program!

class A {
    public String toString() {
        return "A";
    }
    
    public String fancyToString() {
        return "~~A~~";
    }
}

class B extends A {
    public String toString() {
        A letterA = this;
        return letterA.fancyToString();
    }
    public String fancyToString() {
        return "~~B~~";
    }
}

public class LetterPrinter {
    public static void main(final String args[]) {
        B letterB = new B();
        System.out.print(letterB.toString() + " ");
        
        A letterA = letterB;
        System.out.println(letterA.toString());
    }
}

Answer

  • The output of the program is ~~B~~ ~~B~~.
  • With polymorphism, letterB.toString() will make letterA of A letterA = this; in class B to class B, which results in letterA.fancyToString(); return ~~B~~. Similarly, in the main function, when A letterA = letterB, it is morphed into class B rather than class A, resulting in letterA.toString() give the same output as the first print statement ~~B~~.

4

What is the output of this program? You should be able to do this without running the program!

class A {
    public String toString() {
        return "A";
    }
    
    public String fancyToString() {
        return "~~A~~";
    }
}

class B extends A {
    public String fancyToString(){
        return "~~B~~";
    }
}

public class LetterPrinter {
    public static void main(final String args[]) {
        B letterB = new B();
        System.out.print(letterB.toString() + " ");
        
        A letterA = letterB;
        System.out.println(letterA.toString());
    }
}

Answer

  • The output of this program is A A.
  • Note that class B does not have a toString() method, which means calling letterB.toString() will result in the toString() method of class A since class B extends A. Similarly, although A letterA = letterB morphed letterA to class B, it still uses the toString() method of class A.

5

What happens to the object letterA points to in memory when you do the cast below?

class A {
    public String toString() {
        return "A";
    }
}

class B extends A {
    public String toString() {
        return "B";
    }
}

public class PolymorphicOverload {

    public static void main(String args[]) {
        B letterB = new B();
        A letterA = (A) new B();
    }
}

Answer

The cast will have no effect; letterA still points to an object of type B in memory. The cast is not necessary here, and in general is simply a promise to the compiler, not something that updates memory.

6

Consider the following classes

public class A {
    public int foo() {
        return 42;
    }

    public int bar() {
        return foo() + 8;
    }
}

public class B extends C {
    public int foo() {
        return 41;
    }

    public char baz() {
        return 'y';
    }
}

public class C extends A {
    public char baz() {
        return 'x';
    }
}

public class D extends A {
    public int bar() {
        return 7;
    }
}

public class E extends C {
    public int bar() {
        return foo() + 20;
    }
}

Consider a mystery function that returns a object of the given class. You do not know the definition of the mystery function, other than it compiles properly and returns an object of the class. For each of the following method calls marked below, indicate the value of the output, if the output cannot be determined, or if there is an error.


A a = mysteryA(); //<-- mystery function, this line compiles (the below may not!)
System.out.println(a.foo()); //<-- Mark A.1
System.out.println(a.bar()); //<-- Mark A.2
System.out.println(a.baz()); //<-- Mark A.3


B b = mysteryB(); //<-- mystery function, this line compiles (the below may not!)
System.out.println(b.foo()); //<-- Mark B.1
System.out.println(b.bar()); //<-- Mark B.2
System.out.println(b.baz()); //<-- Mark B.3

D d = mysteryD(); //<-- mystery function, this line compiles (the below may not!)
System.out.println(d.foo()); //<-- Mark D.1
System.out.println(d.bar()); //<-- Mark D.2
System.out.println(d.baz()); //<-- Mark D.3

Answer

A.1 : Compiles, Output not deterministic
A.2 : Compiles, Output not deterministic
A.3 : Doesn't Compile, No output

B.1 : Compiles, Output is 41
B.2 : Compiles, Output is 49
B.3 : Compiles, Output is y

D.1 : Compiles, Output is 42
D.2 : Compiles, Output is 7
D.3 : Doesn't Compile, No output

7

What is the difference between a class and an abstract class? From a software engineering perspective, why would you ever want to use an abstract class instead of a regular class?

Answer

An abstract class can only be inherited by another class and can not be directly instantiated. A class can be instantiated in most contexts, and still inherited by other classes as well. abstract classes must also be implemented before code will compile.

You would prefer an abstract class when you want to enforce that the abstract class’ constructor can/should never be called.

8

If you were to create an abstract class for a Car – what features could be defined in the implemented class vs. what could be defined in the abstract class? Provide justifications for your design.

Answer

Features such as car brand, model, year, and other attributes that would be highly variable would be defined in the class implmentation. Other functions that would be mostly fixed and more deterministic across different class definitions, such as toString and functions that get or set values would be defined in the abstact class.

9

What is the instanceof operator in Java? When should you use it? When should you not use it?

Answer

The instanceof operator allows you to look up the runtime type of any object, which may be different than the compile-time type of the same object.

It’s useful when you want to be sure that a cast you plan to do is compatible with the runtime type of the object. However, with good OOP, you should not be relying on this operator for situations that could be better handled by elegant polymorphism.