I don't know why this compile. On the other hand, I can explain how you can fully leverage the compile-time checks.
So, newList()
is a generic method, it has one type parameter. If you specify this parameter, then the compiler will check that for you:
Fails to compile:
String s = Main.<String>newList(); // this doesn't compile anymore
System.out.println(s);
Passes the compile step:
List<Integer> l = Main.<ArrayList<Integer>>newList(); // this compiles and works well
System.out.println(l);
Specifying thetype parameter
The type parameters provide compile-time checking only. This is by design, java uses type erasure for generic types. In order to make the compiler work for you, you have to specify those types in the code.
Type parameter at instance-creation
The most common case is to specify the patterns for an object instance. I.e. for lists:
List<String> list = new ArrayList<>();
Here we can see that List<String>
specifies the type for the list items. On the other hand, new ArrayList<>()
doesn't. It uses the diamond operator instead. I.e. the java compiler infers the type based on the declaration.
Implicit type parameter at method invocation
When you invoke a static method, then you have to specify the type in another way. Sometimes you can specify it as a parameter:
public static <T extends Number> T max(T n1, T n2) {
if (n1.doubleValue() < n2.doubleValue()) {
return n2;
}
return n1;
}
The you can use it like this:
int max = max(3, 4); // implicit param type: Integer
Or like this:
double max2 = max(3.0, 4.0); // implicit param type: Double
Explicit type parameters at method invocation:
Say for instance, this is how you can create a type-safe empty list:
List<Integer> noIntegers = Collections.<Integer>emptyList();
The type parameter <Integer>
is passed to the method emptyList()
. The only constraint is that you have to specify the class too. I.e. you cannot do this:
import static java.util.Collections.emptyList;
...
List<Integer> noIntegers = <Integer>emptyList(); // this won't compile
Runtime type token
If none of these tricks can help you, then you can specify a runtime type token. I.e. you provide a class as a parameter. A common example is the EnumMap:
private static enum Letters {A, B, C}; // dummy enum
...
public static void main(String[] args) {
Map<Letters, Integer> map = new EnumMap<>(Letters.class);
}