Generic Cell “implementation”
How do you define a “generic” Cell class to hold any object? You don’t want to define similar classes with duplicate code: StringCell, Pointcell.
class Cell {
private final Object obj;
Cell(Object obj) {
this.obj = obj;
}
Object get() {
return this.obj;
}
}
Explicit cast
Calling method get() on a Cell returns an Object type, which doesn’t have length method.
jshell> new Cell("chocolate").get().length()
| Error:
| cannot find symbol
| symbol: method length()
| new Cell("chocolate").get().length()
| ^-------------------------------^
The solution
jshell> ((String) new Cell("chocolate").get()).length()
$.. ==> 9
Generic Type = Parametric Polymorphism
Allow a type of method to operate on objects of various types while providing compile-time type safety.
Type parameter section <T1, T2, T3> Type parameters can then be used anywhere within the class/interface.
Generic Classes
Now, you don’t need explicit casting. **The compiler tracks the actual type through the container.
class Cell<T> {
private final T t;
Cell(T t) {
this.t = t;
}
T get() {
return t;
}
@Override
public String toString() {
return "[" + t + "]";
}
}
jshell> Cell<String> cell = new Cell<String>("abc")
cell ==> [abc]
jshell> cell.get().length()
$.. ==> 3
Generic Method
jshell> <T> Cell<T> of(T t) { return new Cell<T>(t); }
| created method of(T)
jshell> Cell<String> cell = of("abc")
cell ==> [abc]
class Cell<T> { // class-level T
private final T t;
...
static <T> Cell<T> of(T t) { // method-level T (should use U)
return new Cell<T>(t);
}
}
There are actually two different Ts here that just happen to share the same name. The class-level T is the one used by instance fields and methods. The method-level <T> (declared right after static) is a completely separate type variable scoped only to that method.
Substitutability
Remember about generic invariance. Even if A is a subtype of B, Method<A> is not a subtype of Method<B>
<? extends T> and Covariant Rule (for Reading)
Reading: getting a value out of the cell (cell.get)
jshell> void foo(Cell<? extends Burger> cell) {
...> Burger b = cell.get();
...> }
jshell> foo(new Cell<FishBurger>(new FishBurger())) works now!
Covariant Rule
If E1 <: E2 (the element type is a subtype) and S <: T (the container type is a subtype), then S<E1> <: T<? extends E2>.
FishBurger <: Burger (given), and Cell <: Cell (trivially), so Cell<FishBurger> <: Cell<? extends Burger>.
When you call cell.get() on a Cell<? extends Burger>, you always get back something that is a Burger. If the actual cell holds a FishBurger, that’s fine because FishBurger is a Burger.
<? super T> and Covariant Rule (for Writing)
Writing: putting a value into the cell (cell.set).
jshell> void bar(Cell<? super Burger> cell) {
...> cell.set(new FishBurger());
...> }
jshell> bar(new Cell<FastFood>(new Burger())) works now!
Contravariant Rule
If E2 <: E1 and S <: T, then S<E1> <: T<? super E2>.
Burger <: FastFood and Cell <: Cell, so Cell<FastFood> <: Cell<? super Burger>.
Substitutability For Reading and Writing
jshell> void baz(Cell<Burger> cell1, Cell<Burger> cell2) {
...> cell1.swap(cell2);
...> }
baz can only take in Cell<Burger> and nothing else! – too general and you cannot get (or read) from the cell; – too specific and you cannot set (or write) to the cell.
Cell<T> is the strongest (most restrictive) generic type – allowed to only read and write elements of type T with Cell<T>
Invariant Rule
If E1 = E2 (the element types are exactly equal) and S <: T (the container type is a subtype), then S<E1> <: T<E2>.
U = T (given), and Cell <: Cell (trivially) so Cell<U> <: Cell<T>.
Substitutability of Cell for No Access
jshell> void qux(Cell<?> cell) {
...> cell.get().toString();
...> }
Cell<?> is the weakest (least restrictive) generic type – not allowed to access elements of type T, or – at most read it as an Object
No access rule
If S <: T (the container type is a subtype), then S<E> <: T<?> for any E. Cell <: Cell (trivially), so Cell<anything> <: Cell<?>.
The Full Spectrum
Cell<T> — exact type, read and write with type T Cell<? extends T> — accepts subtypes, read only (get returns T) Cell<? super T> — accepts supertypes, write only (get returns Object) Cell<?> — accepts anything, no typed access (get returns Object, can’t set anything except null)