Difference between revisions of "Generics"
From Suhrid.net Wiki
Jump to navigationJump to searchLine 132: | Line 132: | ||
* List<?> list is different from List<Object> objList ! The second will take only lists which contain Objects, whereas the former will take anything. | * List<?> list is different from List<Object> objList ! The second will take only lists which contain Objects, whereas the former will take anything. | ||
* It follows that List<? extends Object> is the same as List<?> | * It follows that List<? extends Object> is the same as List<?> | ||
+ | |||
+ | <u> Generic Declarations </u> | ||
+ | |||
+ | * Generic class. See example below: | ||
+ | * The Generic type T's object is treated only as a regular Object. (Obviously, since at compile-time, we are unaware of its type) | ||
+ | |||
+ | <syntaxhighlight lang="java5"> | ||
+ | class Node<T> { | ||
+ | private T t; | ||
+ | |||
+ | public Node(T t) { | ||
+ | this.t = t; | ||
+ | } | ||
+ | |||
+ | public T getNode() { | ||
+ | return t; | ||
+ | } | ||
+ | |||
+ | public String toString() { | ||
+ | return t.toString(); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | public class GenericDeclarations { | ||
+ | |||
+ | public static void main(String[] args) { | ||
+ | Node<Integer> ni = new Node<Integer>(1); | ||
+ | Node<JButton> njb = new Node<JButton>(new JButton()); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
[[Category:OCPJP]] | [[Category:OCPJP]] |
Revision as of 04:48, 2 June 2011
- Generics is a way to enforce ONLY compile-time type safety.
- All the type information is not present at run-time. The compiler strips out type information from the bytecode using a process called type erasure.
- WHY Type erasure ? To ensure backward compatibility with legacy code.
- This compile-time safety is broken when generic and non-generic legacy code are mixed up.
See below:
private void bar() {
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(1));
li.add(new Integer(2));
foo(li);
for(Integer i : li) {
System.out.println(i.intValue()); // This will fail with a ClassCastException.
}
}
private static void foo(List l) {
l.add(new Integer(3));
l.add(new String("4")); //Compiler ALLOWS this ! However, warning will be generated.
}
- Watch out when autoboxing is involved with legacy code.
List l = new ArrayList();
l.add(123); //Auto-boxing happens.
int i = l.get(0); //Compile-time error. Autounboxing cant work because get() returns Object and not Integer.
Polymorphism and Generics
List<String> list = new ArrayList<String>();
Polymorphism only applies to the base type i.e list can be declared as arraylist You CANNOT do this:
List<Animal> obj = new ArrayList<Dog>(); //NOT POSSIBLE
WHY ?
To prevent scenarios where you cannot add say, a Cat object to a Dog List. If the above conversion were possible it will be possible to do so. See below:
//NOTE : This is not possible actually, because the compiler prevents it.
public void foo() {
List<Dog> dList = new ArrayList<Dog>();
addAnimal(dList); //Compiler flags an error here. a Dog list cannot be assigned to an Animal list
}
private void addAnimal(List<Animal> aList) {
aList.add(new Cat());
}
However, the SAME thing is possible with Arrays
public void foo() {
Dog[] dA = new Dog[]{};
addAnimal(dList);
}
private void addAnimal(Animal[] aa) {
aa[0] = new Cat(); //This will cause a runtime ArrayStoreException
}
- The reason why it such polymorphism is possible with Arrays but not with collections is because of Type Erasure.
- Since there is no type information at run-time, JVM cannot raise an exception.
- This will be exactly the same problem when type-safe collections are mixed with non-type safe ones.
- So, the compiler will prevent such polymorphic assignments when we are dealing with type-safe collections.
- Q : How to use generic collections polymorphically then ?
- A : Use wildcards ( ? )
- See example below. This means all Lists of Dogs, Cats, Elephants etc can be passed to the move method.
- However the compiler ensures that the list cannot be modified to avoid wrong animals being inserted into the wrong lists.
- How ? Any method in the List interface which accepts a generic parameters like add(E) or bar(E) will not be allowed.
public static void foo() {
List<Dog> ld = new ArrayList<Dog>();
foo(ld);
}
private static void move(List<? extends Animal> wildL) {
for(Animal a : wildL) {
a.move();
}
}
- If you do want to modify the list, the wildcard declaration must be made safe by using the keyword super.
- In the below example, it means that all Lists containing objects of superclasses of Dog can be passed to the foo method.
- So foo is safe to add Dog objects to the passed list, since the passed list is guaranteed to be a list of some objects which are above Dog. So type-safety is maintained.
public static void bar() {
List<Dog> ld = new ArrayList<Dog>();
foo(ld);
}
private static void foo(List<? super Dog> dogList) {
dogList.add(new Dog());
dogList.add(new Animal()); //WONT WORK.
}
- NOTE VERY IMPORTANT: Wildcards specify what kind of collections the method can accept. NOT what can be added to the collection
- Only using the wildcard - like below, means that any list can be passed to it. But of course nothing can be added to the list.
void foo(List<?> anyList)
- List<?> list is different from List<Object> objList ! The second will take only lists which contain Objects, whereas the former will take anything.
- It follows that List<? extends Object> is the same as List<?>
Generic Declarations
- Generic class. See example below:
- The Generic type T's object is treated only as a regular Object. (Obviously, since at compile-time, we are unaware of its type)
class Node<T> {
private T t;
public Node(T t) {
this.t = t;
}
public T getNode() {
return t;
}
public String toString() {
return t.toString();
}
}
public class GenericDeclarations {
public static void main(String[] args) {
Node<Integer> ni = new Node<Integer>(1);
Node<JButton> njb = new Node<JButton>(new JButton());
}
}