W5: Interfaces and generics
Please submit your answers to the questions as comments in a W5.md
file you’ll be writing in this lecture.
Grading rubric and submission
When you are done, submit your W5.md
file to BB.
You will be graded on the following:
Item | Points |
---|---|
Answers are correct | 100 |
Questions
1
Consider the class declaration below:
// Is this possible?
public class Lion extends Mammal, Carnivore {
// ...
}
- Explain why the following class declaration is not possible in Java.
- How can you accomplish this inheritance structure task in Java?
Answer
Java classes can only extend from one class! So we can have Lion extends Mammal
or Lion extends Carnivore
, but not both. To accomplish functionality similar to this, we would have:
public class Lion extends Mammal implements Carnivore {
// ...
}
As it makes sense for an animal to inherit from the specific class it is (Mammalia), and for it to have an interface that represents how it interacts (or interfaces) with food.
2
What are some of the functional differences between an abstract class
and an interface
? Use the example below to answer this question.
public abstract class Employee {
// ...
}
// vs.
public interface Employee {
//...
}
Answer
Abstract classes and interfaces can:
- Provide just method declarations that a class must realize
Abstract classes do (and interfaces do not):
- Provide methods and constructors a child class can choose to use, or override
- Provide inherited class variables
- Have access modifiers for inherited methods
- Require the child class to inherit only itself, and no other class
3
Consider the interfaces for a Stack
and Queue
of Object
s.
public interface Stack {
public void push(Integer v);
public Integer pop();
public Integer peek();
}
public interface Queue {
public void enqueue(Integer v);
public Integer dequeue();
public Integer peek();
}
Now suppose you had a LinkedList
implementation to store Object
s with the following methods defined.
public class LinkedList implements Stack, Queue {
public LinkedList() {/*...*/}
public void addToFront(Integer v) {/*...*/}
public Integer rmFromFront() {/*...*/}
public void addToBack(Integer v) {/*...*/}
public void rmFromBack() {/*...*/}
//FINISH HERE
}
Using those methods in LinkedList
complete the realization of a Stack
and Queue
:
Answer
public void push(Object v) {
addToFront(v);
}
public Object pop() {
rmFromFront(v);
}
public void enqueue(Object v) {
addToBack(v);
}
public Object dequeue() {
rmFromFront(v);
}
public Object peek() {
Object v = rmFromFront(v);
addToFront(v);
return v;
}
4
Rewrite the Stack
and Queue
interfaces from above to be generic, as well as the LinkedList
. Explain how this is now generic to manage collections of any class.
Answer
public interface Stack<T> {
public void push(T v);
public T pop();
public T peek();
}
public interface Queue<T> {
public void enqueue(T v);
public T dequeue();
public T peek();
}
public class LinkedList<T> implements Stack<T>, Queue<T> {
public LinkedList() {/*...*/}
public void addToFront(T v) {/*...*/}
public T rmFromFront() {/*...*/}
public void addToBack(T v) {/*...*/}
public void rmFromBack() {/*...*/}
public void push(T v) {
addToFront(v);
}
public T pop() {
rmFromFront(v);
}
public void enqueue(T v) {
addToBack(v);
}
public T dequeue() {
rmFromFront(v);
}
public T peek() {
= rmFromFront(v);
T v addToFront(v);
return v;
}
}
5
The code below does not use Java generics. Update it to do so. Notably, there should not need casting, but no, the solution isn’t just removing the (String)
casting before the .get
method.
import java.util.HashMap;
public class TestHashMap {
public static void main (String[] argv) {
// Create a new hashmap.
HashMap fabFour = new HashMap();
// Insert four key and value pairs.
.put("John", "John Lennon");
fabFour.put("Paul", "Paul McCartney");
fabFour.put("George", "George Harrison");
fabFour.put("Ringo", "Ringo Star");
fabFour
// Use a key to retrieve a value.
String fullName = (String) fabFour.get("Ringo");
// Prints "Ringo Star"
System.out.println(fullName);
}
}
Answer
public class TestHashMap {
public static void main(String[] argv) {
// Create a new hashmap.
HashMap<String,String> fabFour = new HashMap<String,String>();
// Insert four key and value pairs.
.put("John", "John Lennon");
fabFour.put("Paul", "Paul McCartney");
fabFour.put("George", "George Harrison");
fabFour.put("Ringo", "Ringo Star");
fabFour
// Use a key to retrieve a value.
String fullName = fabFour.get("Ringo");
// Prints "Ringo Star"
System.out.println(fullName);
}
}
6
What is “erasure” with java generics?
For the code below, what does the code “erase” to?
public static void main(final String args[]) {
<String> favorite_words = shelfBuilder();
Shelf.addItem("Zoetrope");
favorite_words.addItem("Succinct");
favorite_words//...
String s = favorite_words.getItem(1);
System.out.println(s);
}
Answer
The Java runtime actually doesn’t know anything about generics, when you compile a Java program with generics in them, at compilation time all “generic” types are replaced with what they should be. Essentially, when we use a generic, at compilation time the compiler essentially changes the Shelf
class to only use the String
type, so wherever we see a T
it’s replaced with String
. From there, every time we do something that should return as a specific type T
, we replace with a cast to that specific type.
We see this program then erases to:
public static void main(final String args[]) {
= shelfBuilder();
Shelf favorite_words .addItem((String)"Zoetrope");
favorite_words.addItem((String)"Succinct");
favorite_words//...
String s = (String)favorite_words.getItem(1);
System.out.println(s);
}
7
Finish the main
method in the TestShelf
class above.
Expected output:
Shakespeare Characters: Hamlet Othello Cordelia Juliet
Famous Integers: 13 23 42 1729
import java.util.ArrayList;
import java.util.List;
public class Shelf<T> {
private List<T> shelfItems;
private String shelfName;
public Shelf(String shelfName) {
this.shelfName = shelfName;
= new ArrayList<T>();
shelfItems }
public int addItem(T item) {
.add(item);
shelfItemsreturn shelfItems.size();
}
public void printShelf() {
System.out.print(shelfName + ": ");
for(T item: shelfItems) {
System.out.print(item.toString() + " ");
}
System.out.println();
}
}
public class TestShelf {
public static void main(final String args[]) {
// TODO: Create a shelf to store Shakespeare character names:
// Hamlet, Othello, Cordelia, and Juliet
// TODO: Then print the shelf.
// TODO: Create a shelf to store famous integers:
// 13, 23, 42, 1729,
// TODO: Then print the shelf.
}
}
Answer
public static void main(String[] args) {
<String> shakespeare = new Shelf<>("Shakespeare Characters");
Shelf.addItem("Hamlet");
shakespeare.addItem("Othello");
shakespeare.addItem("Cordelia");
shakespeare.addItem("Juliet");
shakespeare<Integer> integers = new Shelf<>("Famous Integers");
Shelf.addItem(13);
integers.addItem(23);
integers.addItem(42);
integers.addItem(1729);
integers.printShelf();
shakespeare.printShelf();
integers}
8
Add the compareTo
method in the Car
class above. So that the main method will print out:
Name: Lamborghini Top Speed: 225
Name: Porsche Top Speed: 202
Name: Mustang Top Speed: 144
Name: Jeep Top Speed: 110
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Car implements Comparable<Car> {
public static void main(String[] args) {
List<Car> carsList = new ArrayList<>();
.add(new Car("Porsche", 202));
carsList.add(new Car("Jeep", 110));
carsList.add(new Car("Mustang", 144));
carsList.add(new Car("Lamborghini", 225));
carsList
Collections.sort(carsList);
for(Car car : carsList) {
System.out.println("Name: " + car.getName() + " Top Speed: " + car.getTopSpeed());
}
}
private String name;
private Integer topSpeed;
public Car(String name, Integer topSpeed) {
this.name = name;
this.topSpeed = topSpeed;
}
public String getName() {
return name;
}
public Integer getTopSpeed() {
return topSpeed;
}
// TODO: Complete the Car class by adding the compareTo method
// needed to correctly implement Comparable<Car>.
}
Answer
public int compareTo(Car other) {
return this.topSpeed - other.topSpeed;
}