Methods and generics with variadic arguments were released simultaneously in Java 5, but they don't go together well. The variadic argument is leaky abstraction. https://euske.github.io/slides/sem20170627/index.html When you call a method with variadic arguments, an array is created to hold the variadic arguments, and this array is visible. (When abstracted, it seems to be called leaky abstraction, which means that the user should not be aware of it.) As a result, if you pass a generic or parameterized type as a variadic argument, the compiler gets confused. If you declare a method with a non-reifiable type as a variadic argument, the compiler raises a warning. The warning is also raised when a method is called that is passed a variadic argument with a type that is inferred to be non-reifiable. The warning is as follows.
warning: [unchecked] Possible heap pollution from
parameterized vararg type List<String>
Heap pollution occurs when various parameterized types refer to a different type. Heap pollution causes the compiler to automatically cause cast failures, violating the type-safe foundations guaranteed by generic types.
As an example, consider the following method.
// Mixing generics and varargs can violate type safety!
static void dangerous(List<String>... stringLists) {
List<Integer> intList = List.of(42);
Object[] objects = stringLists;
objects[0] = intList; // Heap pollution
String s = stringLists[0].get(0); // ClassCastException
}
This method does not have a clear cast, but I get a `ClassCastException```. An implicit cast by the compiler was performed on the last line, failing. This indicates that it is ** unsafe to store a value in a variadic argument of type **. The reason why a compile error does not occur when a method with a variadic argument of a generic type is declared is that variadic arguments of a generic type or a parameterized type are very convenient. As an example, ```Arrays.asList (T ... a)
,
Collections.addAll (Collection <? Super T> c, T ... elements) ```, ```EnumSet. There are methods in the Java standard library such as of (E first, E ... rest) ``
, which are type safe.
Prior to Java6, there was no way for method authors to deal with warnings that appear at method invocations with generic variadic arguments. This meant that the caller had to write `@ SuppressWarnings (" unchecked ")`
each time.
In Java7, the SafeVarargs
annotation has been added. By giving this to a method with a generic type variadic argument, the caller will not get a warning. ** The `` `SafeVarargsannotation indicates that the method author promises that the method is type-safe. ** **
safevarargs```In annotation, it's really important to be type-safe, but how should we be sure that it's type-safe?
An array of generic type is generated when a method is called to hold a variable length array. In the processing of a method, it is safe (that is, only a simple transfer from the argument to the method) if it does not store in the array and does not allow references from untrusted code to the array. It can be said that it is safe).
It should be noted that even if nothing is stored in a variadic array, it can threaten type safety. The following example seems to be okay at first glance, but it is dangerous.
// UNSAFE - Exposes a reference to its generic parameter array!
static <T> T[] toArray(T... args) {
return args;
}
The type of this array depends on the compile-time type of the type passed to the method argument, but the compiler does not give enough information to make an accurate decision. This method returns an array of variadic arguments, so heap pollution occurs on the call stack. To think concretely, consider the following method.
static <T> T[] pickTwo(T a, T b, T c) {
switch(ThreadLocalRandom.current().nextInt(3)) {
case 0: return toArray(a, b);
case 1: return toArray(a, c);
case 2: return toArray(b, c);
}
throw new AssertionError(); // Can't get here
}
In compiling this method, the compiler produces code that produces a variadic array for passing two `T``` instances to the ``` toArray``` method. The code places an array of ```Object []
so that any object can be passed by the caller. The ``
toArraymethod simply returns this array to the
pickTwo method, and the `` `pickTwo
method returns this array to the caller. So the pickTwo
method always returns an array of type
ʻObject []` ``.
Consider calling pickTwo
from the main code below.
public static void main(String[] args) {
String[] attributes = pickTwo("Good", "Fast", "Cheap");
}
Regarding this code, neither a compile error nor a warning appears, but when I execute it, I get a `ClassCastException`
even though I have not explicitly cast it. This happens when I'm trying to make it `String [] ``` because the return type of the
pickTwo``` method is ```Object []
``. ..
This example reminds us that ** it is not safe to allow other methods to access a variadic array of generic types **. There are two exceptions to this:
Here's how to use a typical safe generic type variadic argument:
// Safe method with a generic varargs parameter
@SafeVarargs
static <T> List<T> flatten(List<? extends T>... lists) {
List<T> result = new ArrayList<>();
for (List<? extends T> list : lists)
result.addAll(list);
return result;
}
safevarargs
Determining whether to annotate is simple,For all methods with variadic arguments of generic or parameterized typessafevarargs
Should be annotated.. This means thattoarray
Don't write unsafe variadic methods like methods. The safe variadic method satisfies:
Also, the `` `SafeVarargs``` annotation is valid only for methods that are not overridden.
safevarargs
As a correspondence other than using annotation, from item28, variable length argumentlist
It is conceivable to replace it with. Thenflatten
The method changes as follows.
// List as a typesafe alternative to a generic varargs parameter
static <T> List<T> flatten(List<List<? extends T>> lists) {
List<T> result = new ArrayList<>();
for (List<? extends T> list : lists)
result.addAll(list);
return result;
}
The nice thing about this is that it guarantees type-safeness and you don't have to annotate the `` `SafeVarargs``` yourself. The bad news is that the client-side code can be a bit verbose and a bit slower.
Recommended Posts