CS II Lesson 14

Professor Abdul-Quader

Polymorphism

Project 2

  • Book class (done?)
  • Library class (done? finish soon?)
  • Main class: while loop
    • Output menu of choices (add a book, list all books, etc)
    • Get the user’s choice (1 - 5, or a - e if you prefer)
    • Take the appropriate action (using the Library object?)
    • Exit if the user inputs in choice 5 (quit).

Pseudocode

public static void main(String[] args) {
    while (something) {
        printMenu()
        getChoice()
        if (choice == 1) {
            addABook()
        } else if (choice == 2) {
            listAllBooks()
        } else if...
    }
}

Scanner issue

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println("Enter in a number: ");
    int x = sc.nextInt();
    System.out.println("Enter in a name: ");
    String name = sc.nextLine();
    System.out.println("You entered: " + x + ", " + name);
}
  • What happens when we run this? Fix from Chapter 3.
  • Will probably run into this when you add a Book
    • author (String), title (String), year (int).
    • Make sure after every nextInt in your code, you handle this problem.
      • Or: make everything Strings?

find by title?

What’s wrong with this?

for (int i = 0; i < bookList.size(); i++) {
  if (bookList.get(i).getTitle().equals(title)) {
    System.out.println(i + ". " + bookList.get(i));
  } else {
    System.out.println("There are no books in the library entitled \"" + title + "\"");
  }
}

Hints

  • Can assume that there is only one book by that title.
  • If you ever find a book with that title, return.
  • How do you know if you never found it?

findAllByAuthor

  • Can’t assume only one book with that author.
    • So we can’t return after finding one!
  • How do we know if we’ve never seen anything by that author?
  • Hint: keep a boolean variable that checks whether you’ve ever seen this author.
  • In the loop: if you see the author, update the variable and output the book.
  • After the loop?

Exceptions

  • Asynchronous lesson: exceptions.
  • try-catch-finally, try-with-resources, re-throwing.
  • Questions?

Rules of thumb

  • “Checked” vs “unchecked” exceptions: checked can be recovered from, unchecked are “logic errors”
  • Exceptions are for exceptional situations, not as fancy if-thens!

Polymorphism

  • IOException needs to be “caught”, but IllegalStateException doesn’t.
  • How does the compiler know?
  • IOException extends from Exception; IllegalStateException extends from RuntimeException!

Interfaces

An interface is an abstract type. That is, it just describes behaviors, but it is not actually implemented. You cannot instantiate an interface. This is useful when we are defining methods:

Example

public void sort(List<Integer> list) {
    // don't care how the list is implemented
    // just that I have a list of integers
}

public static void main(String[] args) {
    List<Integer> list1 = new LinkedList<Integer>();
    List<Integer> list2 = new ArrayList<Integer>();
    sort(list1);
    sort(list2);
}

Different Implementations

ArrayList and LinkedList are two different implementations of the same List interface.

  • Both of them implement add, get, and size.
  • ArrayList: keeps an array, resizes that array when needed.
  • LinkedList: keeps each item in a “node”, node has a “link” to the next node.
    • add: create a new node, link from the last node.

Implementation Details

  • The differences between an ArrayList and LinkedList are internal.
  • Externally: we can call list.get(10); or list.add("hello"); on either one.
  • Those differences are called “implementation details”

When we write a method, if we don’t care how list is implemented, we can just ask for a List as a parameter.

Comparable

The Collections class has a static method sort, which will sort a list for us. It can sort lists of any type – as long as the type implements the Comparable interface. The Comparable interface specifies just one method: compareTo.

Implementing Comparable

In order to implement the interface in your class:

public class MyClass implements Comparable<MyClass> {

    // instance variables, constructors, and other methods

    public int compareTo(MyClass other) {
        // return a positive number if this > other
        // return 0 if this == other
       // return a negative number if this < other
    }
}

Exercise

Starter code. Implement the “compareTo” method in the Location class. Implement it so that:

  • A Location on a lower street (south) is “less” than a Location with a higher row
  • If two Locations are on the same street, a Location on a lower avenue (to the east) is “less” than a Location with a higher avenue

Output

Run the main method after you have implemented compareTo. It should output:

[10 St and 7 Ave, 15 St and 2 Ave, 15 St and 5 Ave, 25 St and 3 Ave]

How?

  • How does this work? How is the Collections.sort method able to sort a list of Locations when it had no idea what a Location was when it was written?
  • How are we able to plug our code into existing systems?

Polymorphism

The ability for an object to take multiple forms. Example: if we have a method that can calculate the area of a Rectangle, it should work on Squares too!

Rectangle r = new Square(5);
System.out.println(findArea(r));
//... other code

public int findArea(Rectangle r) {
    // ...
}

Introducing Polymorphism

Two primary ways of introducing polymorphism in Java:

  • Implementing interfaces
  • Extending classes (inheritance).

Dynamic Binding

When an object is instantiated, it has a “compile-time” (declared) type and a “run-time type” (the type of the actual object that is instantiated).

int width = 5; int height = 5;
Rectangle r = new Rectangle(width, height);
Square s = new Square(width);
Rectangle r2 = new Square(width);

What are the declared and run-time types of \(r\), \(s\), and \(r2\)?

Exercise

LinkedList<String> linkedList = new LinkedList<>();
ArrayList<String> arrayList = new ArrayList<>();
List<String> abstractList;

Which of the following assignments are legal?

  1. abstractList = linkedList;
  2. abstractList = arrayList;
  3. arrayList = abstractList;
  4. arrayList = linkedList;
  5. linkedList = arrayList;
  6. linkedList = abstractList;

Think about

List<String> list = new ArrayList<>();
list.add(5);
  • What does the compile-time type tell you about the “add” method?
  • What does the run-time type tell you about the “add” method?

Answers

  • The compile-time type tells you that you’re even allowed to call the method. In this case, the compile-time type being List<Integer> tells you that list.add(5) compiles.
  • The run-time type tells you which code is actually invoked. In this case, it says that the add method in the ArrayList class is the one that is actually going to be called.

Inheritance

Inheritance: to make one class inherit from another, we use the extends keyword.

public class Square extends Rectangle {
  • Square extends Rectangle means every Square is a Rectangle.
  • It does not mean that every Rectangle is a Square!

Compile-time vs run-time

  • Every Square object can invoke methods written inside the Rectangle class – but not vice versa!
  • If the compile-time type of an object is Rectangle:
    • You can only invoke methods declared in the Rectangle class.
    • But the actual method that is invoked is based upon the run-time class!
      • That is: the code that is run might be in Square!
  • What methods can you see? compile-time.
  • What code do you run? run-time.

Example

Starter code

public class Square extends Rectangle {
    private int sideLength;

    public Square(int side) {
        super(side, side);
        System.out.println("Constructing a square with side length " + side);
        sideLength = side;
    }
}

Constructors

  • The constructor for Square must invoke the constructor for its “superclass” (Rectangle).
  • It does this by calling super
  • Notice that when we run Main, the print statement inside Rectangle happens twice:
    • Once for the Rectangle object we create
    • Once for the Square object!

Exercise

  1. In the Rectangle class, implement the getArea method, and then uncomment the printAllAreas(rectangles); line inside Main.
  2. Run the Main. How does this correctly compute the areas even for the Square object?
  3. Uncomment the System.out.println statement inside main. What error do you get? Why?

Exercise (Part 2)

  1. Uncomment the printAllShapes(rectangles); line inside Main. Run it once and take a look at the output. Then implement the toString method inside the Square class. When a Square with side length \(x\) is output, it should print out “Square with side length \(x\)”. Run the Main again and see if the output changes. (It should!) Why does it change?

Object class

Every class inherits from the Object class. This provides default implementations of a few commonly used methods:

  • toString: used when we call System.out.println
  • equals: default implementation checks if two objects refer to the same location in memory
  • getClass: returns the run-time class of an Object. (cannot be overridden)

toString

  • Under the hood: println is defined on all Objects
  • println calls a method (which calls another, which, eventually) which calls the toString method on the Object.
  • If we override the toString method, it will use ours!

Class Hierarchies

  • Every class, except Object, has exactly one direct superclass.
  • In our example:
    • The superclass of Square is Rectangle.
    • The superclass of Rectangle is Object.
  • Notice that in Rectangle’s constructor, we don’t have to explicitly invoke super.
  • Object has a default (no-argument) constructor, which is automatically invoked (unless otherwise specified).

Vocabulary

  • method overriding
  • abstraction
  • implementation
  • polymorphism
  • base class / parent class
  • derived class / child class

Implementing vs extending

Interfaces:

  • A class can implement multiple interfaces.
  • (Prior to Java 8) interfaces do not provide implementations, just declarations.
  • Interfaces do not provide any instance variables.
  • Interfaces describe a contract: they guarantee that implementing classes will have certain methods.

Implementing vs extending

Extending classes:

  • A class can extend exactly one other class.
  • Classes “inherit” all members (variables, methods) from their parent class – but can only see the public or protected members!
  • Used when one type is a more specialized version of a another type.

Class relationships

Two main types of class relationships:

  • Inheritance, also known as an “is-a” relationship.
  • Composition, also known as a “has-a” relationship.

Inheritance

  • If Employee and Person are two different classes, and Employee inherits from Person, we say that every Employee is a Person.
  • Liskov’s Substitution Principle: If \(S\) is a subtype of \(T\), then any object of type \(T\) in a program can be replaced with an object of type \(S\) without breaking the program. (Barbara Liskov, MIT)

Composition

Composition is referred to as a “has-a” relationship. That is: a Driver is not a Vehicle, but a Driver does have a Vehicle. Usually implemented by making the Vehicle be an instance variable of the Driver class.

Upcoming

  • Project 2 due Monday 3/30
  • Exam 2 will be Thursday, 4/2
  • Project 3 assigned after Spring Break
// reveal.js plugins