What is it? How do define it? How to use it? Why use it?
What is Generics?
• Generics provides abstraction over Types
> Classes, Interfaces and Methods can be Parameterized by Types (in the same way a Java type is parameterized by an instance of it)
• Generics makes type safe code possible
> If it compiles without any errors or warnings, then it must not raise any unexpected ClassCastException during runtime
• Generics provides increased readability
> Once you get used to it
Definition of a Generic Class: LinkedList<E>
• Definitions: LinkedList<E> has a type parameter E that represents the type of the elements stored in the linked list
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Queue<E>, Cloneable, java.io.Serializable{
private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0;
public E getFirst() {
if (size==0) throw new NoSuchElementException();
return header.next.element;
}
}
Usage of Generic Class: LinkedList<Integer>
• Usage: Replace type parameter <E> with concrete type argument, like <Integer> or <String> or <MyType>
> LinkedList<Integer> can store only Integer or sub-type of Integer as elements
LinkedList<Integer> li = new LinkedList<Integer>();
li.add(new Integer(0));
Integer i = li.iterator().next();
Example: Definition and Usage of Parameterized List interface
// Definition of the Generic'ized
// List interface
//
interface List<E>{
void add(E x);
Iterator<E> iterator();
...
}
// Usage of List interface with
// concrete type parameter,String
//
List<String> ls = new ArrayList<String>(10);
Why Generics?
Non-genericized Code is not Type Safe
// Suppose you want to maintain String
// entries in a Vector. By mistake,
// you add an Integer element. Compiler
// does not detect this. This is not
// type safe code.
Vector v = new Vector();
v.add(new String(“valid string”)); // intended
v.add(new Integer(4)); // unintended
// ClassCastException occurs during runtime
String s = (String)v.get(1);
• Problem: Collection element types
> Compiler is unable to verify types of the elements
> Assignment must have type casting
> ClassCastException can occur during runtime
• Solution: Generics
> Tell the compiler the type of the collection
> Let the compiler do the casting
> Example: Compiler will check if you are adding Integer type entry to a String type collection
>Compile time detection of type mismatch
Usage of Generics
Using Generic Classes: Example 1
• Instantiate a generic class to create type specific object
• In J2SE 5.0, all collection classes are rewritten to be generic classes
// Create a Vector of String type
Vector<String> vs = new Vector<String>();
vs.add(new Integer(5)); // Compile error!
vs.add(new String(“hello”));
String s = vs.get(0); // No casting needed
Using Generic Classes: Example 2
• Generic class can have multiple type parameters
• Type argument can be a custom type
// Create HashMap with two type parametersSub-typing
HashMap<String, Mammal> map = new HashMap<String, Mammal>();
map.put(“wombat”, new Mammal("wombat"));
Mammal w = map.get(“wombat”);
Generics and Sub-typing
• You can do this (using pre-J2SE 5.0 Java)
> Object o = new Integer(5);
• You can even do this (using pre-J2SE 5.0 Java)
> Object[] or = new Integer[5];
• So you would expect to be able to do this (Well, you can't do this!!!)
> ArrayList<Object> ao = new ArrayList<Integer>();
> This is counter-intuitive at the first glance
• Why this compile error? It is because if it is allowed, ClassCastException can occur during runtime – this is not type-safe
ArrayList<Integer> ai = new ArrayList<Integer>();• So there is no inheritance relationship between type arguments of a generic class
ArrayList<Object> ao = ai; // If it is allowed at compile time,
ao.add(new Object());
Integer i = ai.get(0); // This would result in
// runtime ClassCastException
• The following code work
ArrayList<Integer> ai = new ArrayList<Integer>();• Inheritance relationship between generic classes themselves still exists
List<Integer> li2 = new ArrayList<Integer>();
Collection<Integer> ci = new ArrayList<Integer>();
Collection<String> cs = new Vector<String>(4);
• The following code work
• Entries in a collection maintain inheritance relationship
ArrayList<Number> an = new ArrayList<Number>();
an.add(new Integer(5)); // OK
an.add(new Long(1000L)); // OK
an.add(new String(“hello”)); // compile error
Wild card
Why Wildcards? Problem
• Consider the problem of writing a routine that prints out all the elements in a collection
• Here's how you might write it in an older version of the language (i.e., a pre-5.0 release):
static void printCollection(Collection c) {
Iterator i = c.iterator();
for (k = 0; k < c.size(); k++) {
System.out.println(i.next());
}
}• And here is a naive attempt at writing it using generics (and the new for loop syntax): Well.. You can't do this! static void printCollection(Collection<Object> c) {
for (Object o : c)
System.out.println(o);
}
public static void main(String[] args) {
Collection<String> cs = new Vector<String>();
printCollection(cs); // Compile error
List<Integer> li = new ArrayList<Integer>(10);
printCollection(li); // Compile error
}Why Wildcards? Solution• Use Wildcard type argument <?>
• Collection<?> means Collection of unknown type
• Accessing entries of Collection of unknown type with Object type is safe
static void printCollection(Collection<?> c) {
for (Object o : c)
System.out.println(o);
}
public static void main(String[] args) {
Collection<String> cs = new Vector<String>();
printCollection(cs); // No Compile error
List<Integer> li = new ArrayList<Integer>(10);
printCollection(li); // No Compile error} • You cannot access entries of Collection of unknown type other than Object type
static void printCollection(Collection<?> c) {
for (String o : c) // Compile error
System.out.println(o);
}
public static void main(String[] args) {
Collection<String> cs = new Vector<String>();
printCollection(cs); // No Compile error
List<Integer> li = new ArrayList<Integer>(10);
printCollection(li); // No Compile error
}• It isn't safe to add arbitrary objects to it however, since we don't know what the element type of c stands for, we cannot add objects to it.static void printCollection(Collection<?> c) {
c.add(new Object()); // Compile time error
c.add(new String()); // Compile time error
}
public static void main(String[] args) {
Collection<String> cs = new Vector<String>();
printCollection(cs); // No Compile error
List<Integer> li = new ArrayList<Integer>(10);
printCollection(li); // No Compile error
}Bounded Wildcard• If you want to bound the unknown type to be a subtype of another type, use Bounded Wildcard
static void printCollection(Raw Type & Type Erasure
Collection<? extends Number> c) {
for (Object o : c)
System.out.println(o);
}
public static void main(String[] args) {
Collection<String> cs = new Vector<String>();
printCollection(cs); // Compile error
List<Integer> li = new ArrayList<Integer>(10);
printCollection(li); // No Compile error
}
Raw Type
• Generic type instantiated with no type arguments
• Pre-J2SE 5.0 classes continue to function over
J2SE 5.0 JVM as raw type
// Generic type instantiated with type argument
List<String> ls = new LinkedList<String>();
// Generic type instantiated with no type
// argument – This is Raw type
List lraw = new LinkedList();
Type Erasure
• All generic type information is removed in the resulting byte-code after compilation
• So generic type information does not exist during runtime
• After compilation, they all share same class
> The class that represents ArrayList<String>, ArrayList<Integer> is the same class that represents ArrayList
Type Erasure Example Code: True or False?
ArrayList<Integer> ai = new ArrayList<Integer>();Type-safe Code Again
ArrayList<String> as = new ArrayList<String>();
Boolean b1 = (ai.getClass() == as.getClass());
System.out.println("Do ArrayList<Integer> and ArrayList<String> share same class? " + b1);
• The compiler guarantees that either:
> the code it generates will be type-correct at run time, or
> it will output a warning (using Raw type) at compile time
• If your code compiles without warnings and has no casts, then you will never get a
ClassCastException during runtime
> This is “type safe” code
Interoperability
What Happens to the following Code?
import java.util.LinkedList;
import java.util.List;
public class GenericsInteroperability {
public static void main(String[] args) {
List<String> ls = new LinkedList<String>();
List lraw = ls;
lraw.add(new Integer(4));
String s = ls.iterator().next();
}
}
Compilation and Running
• Compilation results in a warning message
> GenericsInteroperability.java uses unchecked or unsafe operations.
• Running the code
> ClassCastException
Creating Your Own Generic Class
Defining Your Own Generic Class
/**
* This is a generic container class. It encapsulates to objects of any type. This can be used
* for transferring of information between classes within a project, without having the need to
* create temporary storage classes.
*/
public class Pair<A,B> implements Serializable {
private static final long serialVersionUID = 1L;
public A first;
public B second;
public Pair() {
}
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
// infer type parameters
public static <A,B> newPair(A first, B second) {
return new Pair<A,B>(first, second);
}
public static <A,B> Pair<A,B>[] newPairArray(Pair<A,B>... pairs) {
return pairs;
}
public A getFirst() {
return first;
}
public void setFirst(A first) {
this.first = first;
}
public B getSecond() {
return second;
}
public void setSecond(B second) {
this.second = second;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Pair(").append(first).append(',').append(second).append(')');
return sb.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((first == null) ? 0 : first.hashCode());
result = prime * result + ((second == null) ? 0 : second.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Pair other = (Pair) obj;
if (first == null) {
if (other.first != null)
return false;
} else if (!first.equals(other.first))
return false;
if (second == null) {
if (other.second != null)
return false;
} else if (!second.equals(other.second))
return false;
return true;
}
///////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
///////////////////////////////////////////////////////////////////////////
private void writeObject(ObjectOutputStream out) throws IOException {
out.write((int)serialVersionUID);
out.writeObject(first);
out.writeObject(second);
}
@SuppressWarnings("unchecked")
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
int s = (int)in.read();
if (s>=1) {
first = (A) in.readObject();
second = (B) in.readObject();
}
}
}
Using Your Own Generic Class
public class MyOwnGenericClass {
public static void main(String[] args) {
final Pair<String, String> summaries = new Pair<String, String>(summaryShort, summaryMedium);
final Pair<String, Publisher> publisher = new Pair<String, Publisher>(organization, publisher);
video.setLicensor(publisher.getFirst());
final Pair<CatalogImage, CatalogImage> images = new Pair<CatalogImage, CatalogImage>(smallImage, largeImage);
final Pair<Date, Integer> publishedDate = new Pair<Date, Integer>(new DateTime(releaseYear, 7, 1, 0, 0).toDate(), releaseYear);
video.setReleaseyear(publishedDate.getSecond());
}
} Points to Remember
- It’s possible to define or declare generic methods in an interface or a class even if the class or the interface itself is not generic.
- A generic class used without type arguments is known as a raw type.
- List<?> is a supertype of any List type, which means you can pass List<Integer>, or List<String>, or even List<Object> where List<?> is expected.
No comments:
Post a Comment