diamond operator |
Diamond Operator
Before java5, you can create collections containing type Object. Collections written without generics are also known as raw collections.List values = new ArrayList();
Problem is, since any class is an Object, you can add anything to List and later forget about what it is -- which could lead to ClassCastException at runtime.
OCPJP>cat test.java
import java.util.*;
public class test{
public static void main(String...args) {
List list = new ArrayList();
list.add(1);
list.add("str");
String a = (String)list.get(0);
}
}
OCPJP>javac test.java
Note: test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
OCPJP>java test
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at test.main(test.java:7)
OCPJP>
Start from java5, you could document expected parameter and return type with generics. Compiler will caught type mismatch issues at compiler time instead of runtime.
List<String> values = new ArrayList<String>();
OCPJP>cat test.java
import java.util.*;
public class test{
public static void main(String...args) {
List<String> list = new ArrayList<String>();
list.add(1);
list.add("str");
String a = (String)list.get(0);
}
}
OCPJP>javac test.java
test.java:5: error: no suitable method found for add(int)
list.add(1);
^
method Collection.add(String) is not applicable
(argument mismatch; int cannot be converted to String)
method List.add(String) is not applicable
(argument mismatch; int cannot be converted to String)
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error
Java 7 introduced shortened form with diamond operator.
List<String> values = new ArrayList<>();
diamond operator is useful when you have a complexed left side:
Map<Map<Client, Location>, Map<Category, Set<Product<String>>>> = new HashMap<>();
Mixing with Legacy Code
When you mix generic code with legacy raw collection code, the code analysis becomes tricky.
OCPJP testers are fond of mixing raw collections and generic collections to confuse you, because if some code uses generics and other code does not, it sometimes compiles but throws runtime exception.
In the above example, generic method catDisplay suppose to only accept List containing Cat. However, in the main function, a raw collection is passed in as a parameter, which contains Object (both Cat and Dog). Due to type erasure, java don't know Dog is passed in until runtime, when it attempts to cast a Dog to a Cat. With generic types, java writes the casts for us.
Type erasure is the java compiler tricks that the compiler replaces all references to List<E> with Object. In other words, after the code compiles, your generics are actually just Objet types. It is type erasure that allows your generic code to be compatible with raw collections. So after compile, the catDisplay method is converted to pre java4 syntax equivalent like the following:
OCPJP testers are fond of mixing raw collections and generic collections to confuse you, because if some code uses generics and other code does not, it sometimes compiles but throws runtime exception.
import java.util.*;
class Dog {}
class Cat {}
public class Cats {
public static void main(String[] args) {
List cats = new ArrayList();
cats.add(new Cat());
cats.add(new Dog());
catDisplay(cats);
}
private static void catDisplay(List<Cat> cats) {
for(Cat cat: cats) { //ClassCastException
System.out.println(cat);
}
}
}
OCPJP>javac Cats.java
Note: Cats.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
OCPJP>java Cats
Cat@70dea4e
Exception in thread "main" java.lang.ClassCastException: Dog cannot be cast to Cat
at Cats.catDisplay(Cats.java:13)
at Cats.main(Cats.java:9)
OCPJP>
In the above example, generic method catDisplay suppose to only accept List containing Cat. However, in the main function, a raw collection is passed in as a parameter, which contains Object (both Cat and Dog). Due to type erasure, java don't know Dog is passed in until runtime, when it attempts to cast a Dog to a Cat. With generic types, java writes the casts for us.
Type erasure is the java compiler tricks that the compiler replaces all references to List<E> with Object. In other words, after the code compiles, your generics are actually just Objet types. It is type erasure that allows your generic code to be compatible with raw collections. So after compile, the catDisplay method is converted to pre java4 syntax equivalent like the following:
OCPJP>cat Cats.java
import java.util.*;
class Dog {}
class Cat {}
public class Cats {
public static void main(String[] args) {
List cats = new ArrayList();
cats.add(new Cat());
cats.add(new Dog());
catDisplay(cats);
}
private static void catDisplay(List objs) {
for(Object obj: objs) {
Cat cat = (Cat)obj; //compiler inserts the cast because type <Cat>
System.out.println(cat);
}
}
}
OCPJP>javac Cats.java
Note: Cats.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
OCPJP>java Cats
Cat@70dea4e
Exception in thread "main" java.lang.ClassCastException: Dog cannot be cast to Cat
at Cats.catDisplay(Cats.java:14)
at Cats.main(Cats.java:9)
OCPJP>