are easy means to introduce unsafe code.
Indeed, consider the following method:
invocation of doSomethingBad() resolves
as Integer. Hence, the argument list is
expected to be of type List<Integer>.
public static void main(String[] args) {
List list = Arrays.asList("foo", "bar", "baz");
doSomethingBad(list, 1, 2, 3);
private static <T> void
doSomethingBad(List<T>
list, T... values) {
}
Download all listings in this issue as text
It looks relatively harmless at first
sight. It takes a list of generic type T and
a variable list of arguments of type T, and
it assigns the first element of them to
the first one of the list. A variable arguments list is simply a primitive array in
the common type of the elements. There
is no need to cast the call to
list.get(0) to
type T because of the generic type concordance. Still, compiling code with such
a method raises an unchecked warning
caused by potential heap pollution.
To illustrate this, let’s consider the
main() method shown in Listing 1 to
invoke the doSomethingBad() method
shown previously.
Formally, heap pollution
happens when a variable of
a parameterized type points
to another one whose type
is not of that parameterized type. In this example,
list is of raw type List, which
is not parameterized, but it
is assigned with the return
value of
Arrays.asList(), which
resolves to List<String> in that
specific case.
Because the variable argument list is made of integers,
the parametric type T in this
JAVA IN ACTION
The impact
of each Java
SE 7 change
was remarkably
balanced with a
scientifically sound
analysis of millions
of lines of existing
Java code.
generics mix safely, because method
implementations do not perform unsafe
operations. Examples within the Java SE
platform classes include
java.util.Arrays
.asList() and
java.util.Collections.addAll().
Such methods only read the variable
argument arrays and put the values into
collections. Yet, calling those methods
generates warnings, which developers
often disabled before Java SE 7 by adding
@SuppressWarnings({"unchecked",
"varargs"}) annotations on the invoking
methods. This practice adds boilerplate
code, and it might remove warnings for
truly unsafe actions inside such methods.
Now, consider the example in
Listing 3.
The compiler generates a warning for
possible heap pollution. However, looking at the implementation, we know that
this code is safe. As the vendor of the
method, as of Java SE 7, we can reflect
this by adding the
java.lang.SafeVarargs
annotation shown in Listing 4. This is
useful both for documentation purposes
and for removing unnecessary warnings.
This annotation has been added in
several places in the standard library
classes, including
java.util.Arrays.asList()
and
java.util.Collections.addAll(), although
it should be noted that in the case of
java.util.Arrays.asList(), the returned list
is not a copy but a list backed with the
array that is passed as an argument.
There are still restrictions on using
@SafeVarargs. It can be applied only to
static methods, final instance methods,
and constructors, all having variable
argument lists. Indeed, there is no point
in applying @SafeVarargs on a method
or constructor with a fixed arguments
arity. Annotation inheritance works only
with classes—not interfaces, instance
methods, or constructors. This makes it
impossible to substitute the safe code
that a vendor provided and annotated
with @SafeVarargs with a potentially
unsafe one. In such cases, the overridden
methods are not transitively declared
to be safe unless their vendor explicitly
decides to do so. This is why such methods should be either static or final.
Finally, compilers are encouraged
to raise warnings when the variable
argument list type is actually reifi-able (for example, foo(String... args)) or
when unsafe operations still happen
in the annotated method, such as an
ABOUT US