java generics
DESCRIPTION
Simple introduction to the basics of Java 1.5 genericsTRANSCRIPT
Generics in Java 5Generics in Java 5
Better Code in Fewer Keystrokes
Jeslie Chermak ([email protected])
Better Code in Fewer Keystrokes
Jeslie Chermak ([email protected])
What is a “generic”?What is a “generic”?• Generics — An enhancement to the type
system that supports operations on objects of various types while providing compile-time type safety. Note that this lesson is for advanced users. Refer to http://java.sun.com/docs/books/tutorial
• This talk is based on information from http://java.sun.com/docs/books/tutorial/extra/generics
• Generics — An enhancement to the type system that supports operations on objects of various types while providing compile-time type safety. Note that this lesson is for advanced users. Refer to http://java.sun.com/docs/books/tutorial
• This talk is based on information from http://java.sun.com/docs/books/tutorial/extra/generics
Why Should I Use It?Why Should I Use It?• COMPILE-TIME TYPE CHECKING!• Compiler enforces type correctness.• Compiler as debugger!
• Faster runtime execution.• Less code to enter, so fewer chances
for programmer error.• More readable code, so easier to
understand later.
• COMPILE-TIME TYPE CHECKING!• Compiler enforces type correctness.• Compiler as debugger!
• Faster runtime execution.• Less code to enter, so fewer chances
for programmer error.• More readable code, so easier to
understand later.
Simple ClassSimple Class
• Create a new class in package com.jcc.training.generics
• Class will implement a traditional “stack” containing Integers
• Only methods are push, pop, top• Allow NULLs in stack
• Create a new class in package com.jcc.training.generics
• Class will implement a traditional “stack” containing Integers
• Only methods are push, pop, top• Allow NULLs in stack
Java 1.4 ImplementationJava 1.4 Implementationpackage com.jcc.training.generics;
import java.util.ArrayList;import java.util.List;
public class Stack { private final List stack = new ArrayList(); public void push(final Integer value) { this.stack.add(value); // NULL
allowed! } public Integer top() { if (this.stack.isEmpty()) throw new IllegalStateException(); return (Integer) this.stack.get(this.stack.size() - 1); } public Integer pop() { final Integer value = this.top(); this.stack.remove(this.stack.size() - 1); return value; }}
package com.jcc.training.generics;
import java.util.ArrayList;import java.util.List;
public class Stack { private final List stack = new ArrayList(); public void push(final Integer value) { this.stack.add(value); // NULL
allowed! } public Integer top() { if (this.stack.isEmpty()) throw new IllegalStateException(); return (Integer) this.stack.get(this.stack.size() - 1); } public Integer pop() { final Integer value = this.top(); this.stack.remove(this.stack.size() - 1); return value; }}
WIBNIFWIBNIF
• Problem: Collection objects always use Object as base type
• Solution: introduce type parameter
• Problem: Collection objects always use Object as base type
• Solution: introduce type parameter
Java 5 ImplementationJava 5 Implementationpackage com.jcc.training.generics;
import java.util.ArrayList;import java.util.List;
public class Stack { private final List<Integer> stack = new ArrayList<Integer>(); // field typed! public void push(final Integer value) { this.stack.add(value); // NULL allowed! } public Integer top() { if (this.stack.isEmpty()) throw new IllegalStateException(); return this.stack.get(this.stack.size() - 1); // no cast! } public Integer pop() { final int value = this.top(); // OOPS! this.stack.remove(this.stack.size() - 1); return value; }}
package com.jcc.training.generics;
import java.util.ArrayList;import java.util.List;
public class Stack { private final List<Integer> stack = new ArrayList<Integer>(); // field typed! public void push(final Integer value) { this.stack.add(value); // NULL allowed! } public Integer top() { if (this.stack.isEmpty()) throw new IllegalStateException(); return this.stack.get(this.stack.size() - 1); // no cast! } public Integer pop() { final int value = this.top(); // OOPS! this.stack.remove(this.stack.size() - 1); return value; }}
WIBNIFWIBNIF
• Problem: classes need to be rebuilt for differing base types
• Solution: introduce formal type parameter and parameterized type (think parameters to a method, but applied at the class level)
• Problem: classes need to be rebuilt for differing base types
• Solution: introduce formal type parameter and parameterized type (think parameters to a method, but applied at the class level)
Improved
ImplementationImproved
Implementationpackage com.jcc.training.generics;
import java.util.ArrayList;import java.util.List;
public class Stack<E> { // class typed! private final List<E> stack = new ArrayList<E>(); // typed by class public void push(final E value) { // typed by class this.stack.add(value); // NULL allowed! } public E top() { // typed by class if (this.stack.isEmpty()) throw new IllegalStateException(); return this.stack.get(this.stack.size() - 1); } public E pop() { // typed by class final E value = this.top(); // typed by class this.stack.remove(this.stack.size() - 1); return value; }}
package com.jcc.training.generics;
import java.util.ArrayList;import java.util.List;
public class Stack<E> { // class typed! private final List<E> stack = new ArrayList<E>(); // typed by class public void push(final E value) { // typed by class this.stack.add(value); // NULL allowed! } public E top() { // typed by class if (this.stack.isEmpty()) throw new IllegalStateException(); return this.stack.get(this.stack.size() - 1); } public E pop() { // typed by class final E value = this.top(); // typed by class this.stack.remove(this.stack.size() - 1); return value; }}
Good NewsGood News
• Each instance of a generic class shares the same code (unlike C++ template)
• Feeds upon itself: generic type parameters can propagate (e.g. List<E> within Stack<E>)
• Stack<E> x = new Stack<E>();
• Each instance of a generic class shares the same code (unlike C++ template)
• Feeds upon itself: generic type parameters can propagate (e.g. List<E> within Stack<E>)
• Stack<E> x = new Stack<E>();
Bad NewsBad News
• Each instance of a generic class shares the same code!• statics can’t access generic type
information• List<String> ls = new List<Object>();• List<Object> ls = new List<String>();
• … Erasures …
• Each instance of a generic class shares the same code!• statics can’t access generic type
information• List<String> ls = new List<Object>();• List<Object> ls = new List<String>();
• … Erasures …
Wildcard typesWildcard types• Old style:
public void printCol(Collection c) {Iterator i = c.iterator();for (i.hasNext()) {
System.out.println(i.next()); }
}
• Can’t do:public void printCol(Collection<Object> c) {
for (Object o : c) { System.out.println(o);
}}
• So use ? as the wildcard (supertype):public void printCol(Collection<?> c) {
for (Object o : c) { System.out.println(o);
}}
• Old style:public void printCol(Collection c) {
Iterator i = c.iterator();for (i.hasNext()) {
System.out.println(i.next()); }
}
• Can’t do:public void printCol(Collection<Object> c) {
for (Object o : c) { System.out.println(o);
}}
• So use ? as the wildcard (supertype):public void printCol(Collection<?> c) {
for (Object o : c) { System.out.println(o);
}}
Bounded Wildcard typesBounded Wildcard types• What about when you want a base type
other than Object?public abstract class Shape {
public abstract void draw(Canvas c);}public class Rectangle extends Shape { … }public class Circle extends Shape { … }
Can’t do (in class Canvas):public void drawAll(List<Shape> shapes) { for (Shape s : shapes) s.draw(this);}
• So use ? extends as the bounded wildcard:public void drawAll(List<? extends Shape> shapes) { for (Shape s : shapes) s.draw(this);}
• What about when you want a base type other than Object?public abstract class Shape {
public abstract void draw(Canvas c);}public class Rectangle extends Shape { … }public class Circle extends Shape { … }
Can’t do (in class Canvas):public void drawAll(List<Shape> shapes) { for (Shape s : shapes) s.draw(this);}
• So use ? extends as the bounded wildcard:public void drawAll(List<? extends Shape> shapes) { for (Shape s : shapes) s.draw(this);}
Generic methodsGeneric methods• Parameterize types based on arguments
static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) {
c.add(o); // Correct
}
}
Now have compiler infer type of T: Object[] oa = new Object[100];
Collection<Object> co = new ArrayList<Object>();
fromArrayToCollection(oa, co); // T inferred to be Object
String[] sa = new String[100];
Collection<String> cs = new ArrayList<String>();
fromArrayToCollection(sa, cs); // T inferred to be String
fromArrayToCollection(sa, co); // T inferred to be Object
• Parameterize types based on argumentsstatic <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) {
c.add(o); // Correct
}
}
Now have compiler infer type of T: Object[] oa = new Object[100];
Collection<Object> co = new ArrayList<Object>();
fromArrayToCollection(oa, co); // T inferred to be Object
String[] sa = new String[100];
Collection<String> cs = new ArrayList<String>();
fromArrayToCollection(sa, cs); // T inferred to be String
fromArrayToCollection(sa, co); // T inferred to be Object
ErasuresErasures• Generic code like this …
public String loophole(Integer x) { List<String> ys = new LinkedList<String>(); List xs = ys;
xs.add(x); // Compile-time unchecked warning return ys.iterator().next();
}
Actually behaves as …public String loophole(Integer x) { List ys = new LinkedList; List xs = ys; xs.add(x); return (String) ys.iterator().next(); // run time error}
• Generics are implemented by the Java compiler as a front-end conversion called erasure!
• Generic code like this …public String loophole(Integer x) {
List<String> ys = new LinkedList<String>(); List xs = ys;
xs.add(x); // Compile-time unchecked warning return ys.iterator().next();
}
Actually behaves as …public String loophole(Integer x) { List ys = new LinkedList; List xs = ys; xs.add(x); return (String) ys.iterator().next(); // run time error}
• Generics are implemented by the Java compiler as a front-end conversion called erasure!
Some minor details …Some minor details …• Working with pre-Java 5.0 code:
http://download-llnw.oracle.com/javase/tutorial/extra/generics/legacy.html
• Lower bounds and multiple bounds: http://download-llnw.oracle.com/javase/tutorial/extra/generics/morefun.html
• Wildcard capture: http://download-llnw.oracle.com/javase/tutorial/extra/generics/morefun.html
• Class literals as runtime types: http://download-llnw.oracle.com/javase/tutorial/extra/generics/literals.html
• Legacy code upgrade issues: http://download-llnw.oracle.com/javase/tutorial/extra/generics/convert.html
• Working with pre-Java 5.0 code: http://download-llnw.oracle.com/javase/tutorial/extra/generics/legacy.html
• Lower bounds and multiple bounds: http://download-llnw.oracle.com/javase/tutorial/extra/generics/morefun.html
• Wildcard capture: http://download-llnw.oracle.com/javase/tutorial/extra/generics/morefun.html
• Class literals as runtime types: http://download-llnw.oracle.com/javase/tutorial/extra/generics/literals.html
• Legacy code upgrade issues: http://download-llnw.oracle.com/javase/tutorial/extra/generics/convert.html