CSE 331: Software Design and Implementation, Autumn 2026
Your job is to deliver code that you know works.
ListQueue, with Object for String19public class ObjectQueue {
34 private List<Object> queue = new ArrayList<Object>();
40 public void join(Object o) {
41 if (queue.contains(o)) {
42 throw new IllegalArgumentException(o + " is in the queue");
43 }
44 queue.add(o);
45 }
46
47 public Object next() {
48 if (queue.isEmpty()) {
49 throw new NoSuchElementException("the queue is empty");
50 }
51 return queue.remove(0);
52 }ObjectQueue20public static void main(String[] args) {
21 // The queue for office hours. Every element is a NetID.
22 ObjectQueue queue = new ObjectQueue();
23 queue.join("ada");
24 queue.join(1234567);
25 queue.join("eve");
26
27 // Later: help each student in turn.
28 while (queue.size() > 0) {
29 String netid = (String) queue.next();
30 System.out.println("now helping " + netid);
31 }
32}(String) is a cast. Why is it needed?java ObjectQueue$ javac ObjectQueue.java $ java ObjectQueue now helping ada Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (…) at ObjectQueue.main(ObjectQueue.java:29)
22ObjectQueue queue = new ObjectQueue();
23queue.join("ada");
24queue.join(1234567);
25queue.join("eve");
26
27// Later: help each student in turn.
28while (queue.size() > 0) {
29 String netid = (String) queue.next();join(1234567)21// The queue for office hours. Every element is a NetID.
22ObjectQueue queue = new ObjectQueue();
29String netid = (String) queue.next();javac's type checker is a tool for checking type safety:(String) says "trust me" at compile time: checked at run time insteadnext returns a non-String67/** A queue kept as one list of the elements in it. */
68class ListQueue<E> implements Queue<E> {
69 private List<E> queue = new ArrayList<E>();
70
71 public int size() {
72 return queue.size();
73 }<E> after the name declares a type parameterE stands for a type the author does not knowE everywhere20Queue<String> queue = new ListQueue<String>();
21queue.join("ada");
22queue.join("eve");
23
24// Later: help each student in turn.
25while (queue.size() > 0) {
26 String netid = queue.next();
27 System.out.println("now helping " + netid);
28}
29
30// The same class again, for a queue of ticket numbers.
31Queue<Integer> tickets = new ListQueue<Integer>();
32tickets.join(41);
33tickets.join(42);
34int ticket = tickets.next();
35System.out.println("now serving ticket " + ticket);$ java GenericQueue now helping ada now helping eve now serving ticket 41
Queue<String>: String is the type argument, so E is Stringnext returns a String: no castQueue<Integer> for the bakery (not int!)Queue<String>ObjectQueue, javac accepted this!20Queue<String> queue = new ListQueue<String>();
21queue.join("ada");
22queue.join(1234567);
23queue.join("eve");$ javac GenericQueue.java GenericQueue.java:22: error: incompatible types: int cannot be converted to String queue.join(1234567); ^ ⋮ 1 error
javacE75public void join(E e) {
76 if (queue.contains(e)) {
77 throw new IllegalArgumentException(e + " is in the queue");
78 }
79 queue.add(e);
80}E is: String here, Integer therejoin compares by equals (in contains), and calls toString38public static <P> P cheapest(List<P> list) {
39 P best = list.get(0);
40 for (P p : list) {
41 if (p.getPrice() < best.getPrice()) {
42 best = p;
43 }
44 }
45 return best;
46}<P> before the return type: a generic methodp.getPrice(): does javac accept this call?P could be any type, and Object has no getPrice38public static <P extends Product> P cheapest(List<P> list) {
39 P best = list.get(0);
40 for (P p : list) {
41 if (p.getPrice() < best.getPrice()) {
42 best = p;
43 }
44 }
45 return best;
46}extends Product is a bound on PP as a Product: getPricejavac accepts it19List<Product> cart = new ArrayList<Product>();
20cart.add(new Product("skittles", 2.0));
24List<SaleProduct> deals = new ArrayList<SaleProduct>();
25deals.add(new SaleProduct("chips", 3.0, 0.5));
28Product p = cheapest(cart);
29System.out.println("cheapest(cart): " + p);
30SaleProduct s = cheapest(deals);
31System.out.println("cheapest(deals): " + s);$ java Cheapest cheapest(cart): m&ms: $1.0 cheapest(deals): gum: $0.5
cheapest(cart): P is Productcheapest(deals): P is SaleProduct, and no castjavac works P out from the argument of each call25 List<SaleProduct> deals = new ArrayList<SaleProduct>();
29 System.out.println("total(cart): $" + total(cart));
30 System.out.println("total(deals): $" + total(deals));
39public static double total(List<Product> cart) {javac accepts a SaleProduct where a Product is expectedtotal(deals): does javac accept a List<SaleProduct> here?javac says to total(deals)$ javac Carts.java Carts.java:30: error: incompatible types: List<SaleProduct> cannot be converted to List<Product> System.out.println("total(deals): $" + total(deals)); ^ ⋮ 1 error
List<SaleProduct> is not a Java subtype of List<Product>get returns a Product. In a List<SaleProduct>: a SaleProductget is no weakeradd takes any Product. In a List<SaleProduct>: only a SaleProductadd is strongerjavac is right to refusejavac is right to refusetotal never adds. So why is javac right to refuse total(deals)?javac checks a call against the parameter's type, not the bodytotal might change later to add a Product21// The deals: sale products only.
22List<SaleProduct> deals = new ArrayList<SaleProduct>();
23deals.add(new SaleProduct("chips", 3.0, 0.5));
24
25// A second name for the same list, declared a List<Product>.
26List<Product> all = deals;
27all.add(new Product("skittles", 2.0));
28
29SaleProduct second = deals.get(1);all = deals: a second name for the one listdeals as a parameterall.add(...): the skittles go in, through alldeals.get(1): they come out of deals, called a SaleProductjavac says to ListCarts$ javac ListCarts.java ListCarts.java:26: error: incompatible types: List<SaleProduct> cannot be converted to List<Product> List<Product> all = deals; ^ 1 error
all.add and deals.get each do what their variable's type allowsjavac refuses the second nameRule: the type arguments must be the same
For a generic class or interface C,
C<B> is accepted where C<A> is expected only if
A and B are the same type.
List<SaleProduct> is not a Java subtype of List<Product>ArrayList<Product> is a Java subtype of List<Product>total with a type parameter20 List<Product> cart = new ArrayList<Product>();
25 List<SaleProduct> deals = new ArrayList<SaleProduct>();
29 System.out.println("total(cart): $" + total(cart));
30 System.out.println("total(deals): $" + total(deals));
39public static <P extends Product> double total(List<P> cart) {
40 double sum = 0.0;
41 for (Product p : cart) {
42 sum = sum + p.getPrice();
43 }
44 return sum;
45}$ java Carts total(cart): $3.0 total(deals): $2.0 ⋮
<P extends Product>: cart is a list of some one type Pjavac accepts both callsjavac checks the body39public static <P extends Product> double total(List<P> cart) {
40 double sum = 0.0;
41 for (Product p : cart) {
42 sum = sum + p.getPrice();
43 }
44 return sum;
45}for (Product p : cart): an element of cart has the type PP is Product or a Java subtype, so p may hold it39public static double total(List<? extends Product> cart) {
40 double sum = 0.0;
41 for (Product p : cart) {
42 sum = sum + p.getPrice();
43 }
44 return sum;
45}total uses P only once, so it needs no name? is a wildcard: a type the method does not name<? extends Product>: cart is a list of some one typetotal may no longer add39public static double total(List<? extends Product> cart) {
40 cart.add(new Product("bag", 0.1));
41 double sum = 0.0;$ javac Carts.java Carts.java:40: error: incompatible types: Product cannot be converted to CAP#1 cart.add(new Product("bag", 0.1)); ^ where CAP#1 is a fresh type-variable: CAP#1 extends Product from capture of ? extends Product ⋮ 1 error
CAP#1 is javac's name for the type that ? stands forjavac refuses the call51public static void addMints(List<? super SaleProduct> list) {
52 list.add(new SaleProduct("mints", 1.0, 0.5));
53}<? super SaleProduct>: list is a list of some one typeList<SaleProduct>, a List<Product>, or a List<Object>list, get returns only an Objectfoo takes these parameters | |
|---|---|
Object | o |
Product | p |
SaleProduct | s |
List<? extends Product> | from |
List<? super SaleProduct> | to |
Statement in foo | Y/N |
|---|---|
p = from.get(0) | |
to.add(s) | |
o = to.get(0) | |
p = to.get(0) | |
to.add(o) | |
from.add(s) |
javac accepts itfrom is a List<? extends Product>to is a List<? super SaleProduct>Statement in foo | Y/N | Why |
|---|---|---|
p = from.get(0) | Y | every element is a Product |
to.add(s) | Y | every list it may be takes a SaleProduct |
o = to.get(0) | Y | every element is an Object |
p = to.get(0) | N | to may be a List<Object> |
to.add(o) | N | to may be a List<SaleProduct> |
from.add(s) | N | from may be a List<ClearanceProduct> |
nullfrom is a List<? extends Product>to is a List<? super SaleProduct>Statement in foo | Y/N | Why |
|---|---|---|
o = from.get(0) | Y | every element is an Object |
from.add(null) | Y | null may go in a list of any type |
o = to.get(0) | Y | every element is an Object |
to.add(null) | Y | null may go in a list of any type |
from, add accepts only nullto, get returns only an Objectjavac accepts the first where the second is expectedjavac refuses, since the type arguments differextends, add through superRule: get through extends, add through super
If a method only gets from a list, declare it a List<? extends A>.
If a method only adds to a list, declare it a List<? super B>.
If a method does both, declare it a List<A>.
addAll takes a Collection<? extends E>Naftalin and Wadler, the Get and Put Principle; Bloch, PECS: producer extends, consumer super
16// The deals: sale products only.
17SaleProduct[] deals = new SaleProduct[2];
18deals[0] = new SaleProduct("chips", 3.0, 0.5);
19
20// A second name for the same array, declared a Product[].
21Product[] all = deals;
22all[1] = new Product("skittles", 2.0);
23
24SaleProduct second = deals[1];
25System.out.println("second deal: " + second);all = deals: arrays are like lists, right? Does javac refuse it?javac accepts it: SaleProduct[] is a Java subtype of Product[]all[1] = new Product(...): what happens?java ArrayCarts$ java ArrayCarts Exception in thread "main" java.lang.ArrayStoreException: Product at ArrayCarts.main(ArrayCarts.java:22)
javac refused the second name: the program never ran21Queue<String> names = new ListQueue<String>();
22Queue<Integer> tickets = new ListQueue<Integer>();
23System.out.println("names: " + names.getClass());
24System.out.println("tickets: " + tickets.getClass());
25
26Object o = tickets;
27System.out.println("Queue<?>: " + (o instanceof Queue<?>));getClass print?java Erasure$ java Erasure names: class ListQueue tickets: class ListQueue Queue<?>: true
javac leaves type arguments out of the code that runs: type erasureListQueueo instanceof Queue<String>: refusedequals tests instanceof Queue<?>new E[10]: refused. ListQueue writes no array: it keeps a List<E>javac checks itclass ListQueue<E><P extends Product>List<SaleProduct> is not a List<Product>: add asks for moreextends, add through super