Suresh Rohan's Blog

This blog is all about Java, J2EE,Spring, Angular, React JS, NoSQL, Microservices, DevOps, BigData, Tutorials, Tips, Best practice, Interview questions, Views, News, Articles, Techniques, Code Samples, Reference Application and much more

Friday, May 31, 2013

Generics


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 parameters
HashMap<String, Mammal> map = new HashMap<String, Mammal>();
map.put(“wombat”, new Mammal("wombat"));
Mammal w = map.get(“wombat”);
Sub-typing

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>();
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
• So there is no inheritance relationship between type arguments of a generic class
• The following code work
ArrayList<Integer> ai = new ArrayList<Integer>();
List<Integer> li2 = new ArrayList<Integer>();
Collection<Integer> ci = new ArrayList<Integer>();
Collection<String> cs = new Vector<String>(4);
• Inheritance relationship between generic classes themselves still exists
• The following code work
      
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
• Entries in a collection maintain inheritance relationship

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
}
More on Wildcards

• 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(
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 & Type Erasure

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>();
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);
Type-safe Code Again

• 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