Why does the following code compile cleanly without any warnings, even with xlint:all
?
class A<V> {
public V v;
public <V> A() {
}
public static <V> A<V> create() {
return new A<V>();
}
}
public class FooMain {
public static void main(String args[]) {
A.create().v = 5;
A.create().v = "a string";
}
}
running:
javac -Xlint:all src/FooMain.java
results in a clean compile without any warnings (unchecked, etc.). What is the type parametrization of the instance of the generic class A
that gets's created on the first line and what on the second? And how does the compiler determine the type? By looking at the type of the r-values?
An unchecked warning tells a programmer that a cast may cause a program to throw an exception somewhere else.
If we can't eliminate the “unchecked cast” warning and we're sure that the code provoking the warning is typesafe, we can suppress the warning using the SuppressWarnings(“unchecked”) annotation. When we use the @SuppressWarning(“unchecked”) annotation, we should always put it on the smallest scope possible.
What does unchecked assignment mean? Unchecked assignment: 'java.util.List' to 'java.util.List<java.lang.String>' It means that you try to assign not type safe object to a type safe variable.
In both of the statements, the compiler does not have enough information to infer the type argument for V
. When this happens, it defaults to V
's upper bound, which is Object
.
The assignments are valid because both 5
and "a string"
are assignable to Object
.
We can show that the type of A.create().v
is Object
by trying to assign it to a more specific type:
Integer i = A.create().v; // Type mismatch: cannot convert from Object to Integer
The error is the same, even if we assign a more specific type first (the result of an assignment expression is the type of the assigned variable):
Integer i = (A.create().v = 5); // same error
Note that a "type witness" can be used to explicitly specify generic type arguments, in the case that they aren't inferred:
Integer i = A.<Integer>create().v; // valid
A.<String>create().v = 5; // incompatible types
A.<Integer>create().v = "a string"; //
These are contrived examples just to demonstrate compiler behavior, but a type witness is occasionally useful, for example:
static void m(List<String> list) { }
public static void main(String args[]) {
m(Collections.emptyList()); // error
m(Collections.<String>emptyList()); // valid
}
Due to these two statement
A.create().v = 5;
A.create().v = "a string";
Compiler will easily resolve in the first case <V>
by <Integer>
and in the second case <V>
by <String>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With