---
title: Java Programming 2 - Generics
tags: java, generics
---
# Java Generics
### References
* https://courses.cs.washington.edu/courses/cse331/18wi/lectures/lec13-generics.pdf
* https://www.oracle.com/technetwork/articles/java/juneau-generics-2255374.html
* https://www.journaldev.com/1663/java-generics-example-method-class-interface
## Why We Need Generics?
* For example, we consider a collection of books like this:
```java=
public class BoxOfBooks {
private Book[] books;
public BoxOfBooks(int size) {
books = new Book[size];
}
public Book get(int idx) {
Book output = books[idx];
books[idx] = null;
return output;
}
public void add(int idx, Book book) {
books[idx] = book;
}
}
```
* Now we consider another collection, say toys:
```java=
public class BoxOfToys {
private Toy[] toys;
public BoxOfToys(int size) {
toys = new Toy[size];
}
public Toy get(int idx) {
Toy output = toys[idx];
toys[idx] = null;
return output;
}
public void add(int idx, Toy toy) {
toys[idx] = toy;
}
}
```
* Stop!!! **Don’t repeat yourself!**
```java=
public class Box<T> {
private T[] items;
public Box(int size) {
items = (T[]) new Object[size]; // Why this works?
}
public T get(int idx) {
T output = items[idx];
items[idx] = null;
return output;
}
public void add(int idx, T item) {
items[idx] = item;
}
}
```
* So we can store books and toys by using this **generic class**.
* You may see Jave Generics widely used in Java Collections.
```java=
public class GenericsDemo1 {
public static void main(String[] args) {
Box<Book> c1 = new Box<>();
c1.add(new Book("Java Programming 2"));
Box<Toy> c2 = new Box<>();
c2.add(new Toy("Snoopy"));
}
}
```
* In this way, we could **reuse** our codes, just like what we learned before.
* For example, loops, methods, and inheritance in OOP.
## Syntax of Generic Classes
* To declare the **type parameters**, we put these type variables in a pair of angle brackets <>.
```java=
class ClassName<T1, T2, ...> {}
interface InterfaceName<T1, T2, ...> {}
```
* For example,
```java=
public interface List<E> {
E get();
void add(E e);
}
```
* Another example shows how to declare multiple type parameters.
```java=
public interface Map<K, V> {
V get(K key);
void put(K key, V value);
boolean contains(K key);
K[] keys();
V[] values();
}
```
* For convention, we use letters like:
* ``E``: element
* ``T``: type
* ``K``, ``V``: key and value
* Could you explain the following code snippet?
```java=
public class Graph<N> extends Iterable<N> {
List<List<N>> neighbors;
public Graph(Set<N> nodes, Set<Tuple<N, N>> edges) { ... }
}
```
## Upper Bound of Type Parameters
* We can put restrictions on the type parameters.
```java=
interface List1<E extends Object> {
...
}
interface List2<E extends Number> {
...
}
public class UpperBoundGenericsDemo {
public static void main(String[] args) {
List1<Date> lst1 = ... // ok!
List2<Date> lst2 = ... // compile-time error!
}
}
```
* As you can see, you cannot use ``Date`` in ``List2<E extends Number>`` because ``Date`` is not a subtype of ``Number``.
* So Java compiler can detect the error before you run the program.
==與其執行的時候才錯誤,不如寫程式的當下就知道有問題!==
* Let ``Type1`` and ``Type2`` (and so on) be any type. Then the syntax of **bounded** type parameters is as follws:
```java=
class ClassName<T1 extends Tyep1, T2 extends Tyep2, ...> {}
interface InterfaceName<T1 extends Tyep1, T2 extends Tyep2, ...> {}
```
* You can try this example:
```java=
public interface Path<N, P extends Path<N, P>> extends Iterable<N>, Comparable<Path<?, ?>> {
public Iterator<N> iterator();
...
}
```
* For multiple upper bounds on one type variable, you can use the operator & to cascade all types required.
```java=
class ClassName<T extends Class1 & Interface2 & Interface3 & ...> {}
```
## Generics & Subtyping
* Assume that ``Integer`` and ``Double`` are subtypes of ``Number``.
```java=
Number number = new Integer(100);
...
number = new Double(3.14);
```
* Now we consider to use these types in generics.
* We proceed to investigate the relation between generics and subtyping.
* Java subtyping is **invariant** with respect to generics: neither covariant nor contravariant.
* Java adopts a conservative way.
* For example, ``List<Integer>`` is not a subtype of ``List<Number>`` and vice versa.
* Rule:
* Let ``X`` be any type.
* If ``T1`` is not identical to ``T2``, then ``X<T1>`` is irrevalent with ``X<T2>``.
==簡而言之,<>內的型態不同時,就沒有繼承關係。==
* We can see the reasons below:
* **Covariance** allows read-only operations. (Why?)
```java=
List<Number> data = new List<Integer>();
Number item = data.get(); // always ok
data.add(5.28); // bazinga!!!
```
* **Contravariance** allows write-only operations. (Why?)
```java=
List<Integer> data = new List<Number>();
Integer item = data.get(); // bazinga!!!
data.add(0); // always ok
```
### More about the parameters
* We have seen ``List<Integer>`` and ``List<Number>`` are not subtype-related.
* But there is subtyping as expected on the generic types themselves.
* If ``HeftyBag`` extends ``Bag``, then
* ``HeftyBag<Integer>`` is a subtype of ``Bag<Integer>``;
* ``HeftyBag<Number>`` is a subtype of ``Bag<Number>``;
* ``HeftyBag<String>`` is a subtype of ``Bag<String>``.
==<>內形態相同時,holder本身的繼承關係是存在的。==
## Syntax of Generic Methods
* Why do we need generic methods?
```java=
public class Util {
public static void sum(List<Number> lst) { ... }
public static void main(String[] args) {
List<Integer> data = ...
sum(data); // goes wrong in compile time
}
}
```
* As seen before, ``List<Integer>`` is not a subtype of ``List<Number>``.
* We can declare a new type parameter for the method.
```java=
public class Util {
public <T extends E> static void sum(List<T> lst) { ... }
public static void main(String[] args) {
List<Integer> data = ...
sum(data); // pretty good!
}
}
```
* Check another example shown below.
```java=
public class Util2 {
public <T> static void copyTo(List<T> dst, List<T> src) {
for (T item: src)
dst.add(item);
}
public static void main(String[] args) {
List<Integer> src = Arrays.asList(10, 20, 30);
List<Integer> dst = new ArrayList<>();
Util.copyTo(dst, src);
}
}
```
* What is the restriction of this solution?
* You can only duplicate the data in the data structure of the same type.
* For example, we can copy data from ``List<Integer>`` to ``List<Integer>``, but not from ``List<Integer>`` to ``List<Number>``.
* How about this example?
```java=
public class Util2 {
public <T1, T2 extends T1> static void copyTo(List<T1> dst, List<T2> src) {
for (T item: src)
dst.add(item);
}
public static void main(String[] args) {
List<Integer> src = Arrays.asList(10, 20, 30);
List<Number> dst = new ArrayList<>();
Util.copyTo(dst, src);
}
}
```
## Wildcard
* **P**roducer **E**xtends **C**onsumer **S**uper (**PECS**)
```java=
public <T> static void copyTo(Collection<? super T> dst, Collection<? extends T> src) {
for (T item: src)
dst.add(item);
}
```
## Comparison to Native Arrays
* Actually, the native array allows covariance.
* However, this leads to troubles which cannot be discovered in compile-time.
* For example,
```java=
public class GenericsDemo2 {
public static void main(String[] args) {
Animal[] animals = new Dog[256]; // covariance
add7(animals, new Cat());
}
public static void add7(Animal[] animals, Animal animal) {
animals[7] = animal;
}
}
```
* In this case, you encounter the **ArrayStoreException** at runtime.
## Restrictions on Java Generics
* Cannot instantiate generic types with primitive types.
* Solution: you need those wrapper classes (``Integer``, ``Double``, ...).
* Cannot create instances of type parameters.
* Solution: injection
* Cannot declare static fields whose types are type parameters.
* Solution:
* Cannot use casts or instanceof With parameterized types.
* Cannot create arrays of parameterized types
* Cannot create, catch, or throw objects of parameterized types
* Cannot overload a method where the formal parameter types of each overload erase to the same raw type
## Application: Object Comparison
```java=
public class ComparisonDemo {
public static void main(String[] args) {
// TBD
}
}
```