This question is specific to using varargs with generic-fied Enum<E>s:
Why do I get this warning Type safety: Potential heap pollution via varargs parameter elements if I define the method the like this:
<E extends Enum<E>> void someMethod(E... elements)
As opposed to this:
<E extends Enum<E>> void someMethod(E[] elements)
Accordingly, what should I look out for before declaring the method #SafeVarargs?
Similar questions
This question is similar to these questions regarding Collection<T>... but the scenarios shown in those answers do not seem to apply with Enum<E>...:
Potential heap pollution via varargs parameter
Type safety: Potential heap pollution via varargs parameter subtrees
Potential heap pollution via varargs parameter
Eclipse different behaviour for "Unchecked" warning due to "Potential heap pollution via varargs parameter". How to fix?
It is the reverse of this question which questions why there are no warning:
Why doesn't the following method produce potential heap pollution?
Sample code
This is what I tried to pollute the heap, but each bad attempt results in java.lang.ArrayStoreException and not a polluted array.
I am using Eclipse 4.6.0 and Java JDK 8u74.
public static void main(String[] args) {
Foo[] x = { Foo.A };
someMethod(x);
Foo y = x[0]; // How does one get a ClassCastException here?
}
private static enum Foo {
A, B, C,
}
private static enum Bar {
X, Y, Z,
}
// This produces a "Type safety" warning on 'elements'
private static <E extends Enum<E>> void someMethod(E... elements) {
Object[] objects = elements;
// Test case 1: This line throws java.lang.ArrayStoreException
objects[0] = "";
// Test case 2: Program terminates without errors
objects[0] = Foo.A;
// Test case 3: This line throws java.lang.ArrayStoreException
objects[0] = Bar.X;
}
There is a warning for the varargs method because varargs methods can cause implicit array creation at the calling site, whereas the version that takes an array parameter doesn't. The way that varargs works is that it causes the compiler to create an array at the call site filled with the variable arguments, which is then passed to the method as a single array parameter. The parameter would have type E[], so the array created should be an E[].
First, in the call site in your example, you are not using the variable arguments functionality at all. You are passing the array of variable arguments directly. So there is no implicit array creation in this case.
Even if you had used the variable arguments functionality, e.g. with someMethod(Foo.A);, the implicit array creation would be creating an array with a reifiable type, i.e. the variable argument type is known at the call site to be Foo which is a concrete type known at compile-time, so array creation is fine.
The problem is only if the variable argument type at the call site is also a generic type or type parameter. For example, something like:
public static <E extends Enum<E>> void foo(E obj) {
someMethod(obj);
}
Then the compiler would need to create an array of this generic type or type parameter (it needs to create an E[]), but as you know generic array creation is not allowed in Java. It would instead create an array with the component type being the erasure of the generic type (in this example it would be Enum), so it would pass an array of the wrong type (in this example, the passed array should be an E[], but the array's actual runtime class would be Enum[] which is not a subtype of E[]).
This potential wrong type of array is not always a problem. Most of the time, a varargs method will simply iterate over the array and get elements out of it. In this case, the runtime class of the array is irrelevant; all that matters is that the elements are type E (which indeed they are). If this is the case, you can declare your method #SafeVarargs. However, if your method actually uses the runtime class of the passed array (for example, if you return the varargs array as type E[], or you use something like Arrays.copyOf() to create an array with the same runtime class), then a wrong runtime class of the array will lead to problems and you cannot use #SafeVarargs.
Your example is a bit weird because, well, you are not even using the fact that the elements are of type E, let alone the fact that the array is E[]. So you could use #SafeVarargs, and not only that, you could simply declare the array as taking Object[] in the first place:
private static void someMethod(Object... objects)
Related
I am doing tasks from the Java SE 8 book by Cay S. Horstmann (Chapter 6 task 21)
Using the #SafeVarargs annotation, write a method that allows you to build arrays of generalized types, as in the following example:
List<String>[] result = Arrays.<List<String> > construct (10) ;
// Sets the result in a list of type List<String>[] with a length of 10
My intended solution was such
static <T> T[] construct(int size) {
ArrayList<String> arr = new ArrayList<>();
for (int i = 0; i < size; i++) arr.add(null);
return (T[]) arr.toArray();
}
but it is incorrect since after erasing occurs
Exception in thread "main" java.lang.ClassCastException: class
[Ljava.lang.Object; cannot be cast to class [Ljava.util.List;
([Ljava.lang.Object; and [Ljava.util.List; are in module java.base of
loader 'bootstrap') at Main.main(Main.java:23)
Don’t use this in production code. This is only for answering the book’s question as-is, for educational purpose. See the end of the answer for details.
The #SafeVarargs does already provide a hint towards the intended solution. Make the method a varargs method. Since a variable number of arguments also allows zero arguments, this change allows unchanged invocations:
public class Example
{
#SafeVarargs
static <T> T[] construct(int size, T... template) {
return Arrays.copyOf(template, size);
}
public static void main(String args[])
{
List<String>[] result = Example.<List<String>>construct(10);
System.out.println(result.getClass().getComponentType());
}
}
Note that you don’t need explicit type parameters here. The caller can be simplified to
List<String>[] result = Example.construct(10);
which implies that we can even omit the declaring class when being in the same class or using import static …
List<String>[] result = construct(10);
But when the caller does provide arguments to the varargs parameter, they may show up in the resulting array. To enforce an array with only null elements, you could use
#SafeVarargs
static <T> T[] construct(int size, T... template) {
return Arrays.copyOf(Arrays.copyOf(template, 0), size);
}
or
#SafeVarargs
static <T> T[] construct(int size, T... template) {
T[] array = Arrays.copyOf(template, size);
Arrays.fill(array, null);
return array;
}
which can be tested with
List<String>[] result = construct(10, Collections.emptyList());
System.out.println(result.getClass().getComponentType());
System.out.println(Arrays.toString(result));
But as said, don’t use this code. The creation of generic arrays is not allowed for a reason. Consider
List<String>[] result = construct(10);
Object[] array = result;
array[0] = Arrays.asList(42);
List<String> stringList = result[0];
// this situation is called heap pollution
// at an entirely different place in you application, you might do
String s = stringList.get(0);
// without understanding what's going on
The reason why this code does not produce a warning, is that the #SafeVarargs prevents this and is used incorrectly here. For a varargs method to be safe, we must prevent any assignment to the array (this applies to copies of the array as well), but as soon as we return a generic array, we can’t guaranty this. As the user newacct correctly pointed out, there’s not even a guaranty that the array’s raw element type matches the intended type, e.g. if the caller also uses a type variable for T. If this has an impact on a method’s behavior, it must not use the #SafeVarargs annotation.
It's a common mistake. arr.toArray() returns Object[] and then you try to cast it to List[] array. Unlike generics the type of arrays is not removed during runtime. That's why you get a class cast exception.
It's not possible to write such a method in Java that will work in all circumstances, and I can prove it. The mention of #SafeVarargs indicates that they want you to use varargs, and the varargs semantics does allow you to do some interesting things, but it will still not allow you to write such a method that works in all circumstances.
When you have a varargs method in Java like someMethod(int size, String... args), it receives a second argument of type String[], and when you call it with (0 or more) String arguments (and not a String[] argument directly), the compiler will implicitly create a new array like new String[] { ... } and pass that to the method under the hood.
The varargs parameter can also be of type variable type like someMethod(int size, T... args). If you call this method with 0 or more T arguments, the compiler will also try to implicitly create an array of the arguments. If the compiler knows (whether by inference or you explicitly specified) that for this call T is a reifiable type like String, then it will do new String[] { ... } with no problems.
However, when the compiler's (inferred or explicitly specified) T for this call is a parameterized type or a type variable, there is a problem because Java does now allow creating an array of a parameterized type (e.g. new List<String>[...]) or an array of a type variable type (e.g. new U[...]). What Java does is implicitly create an array with the component type being the erasure of what it inferred T to be. So if T for this call is List<String>, it will create new List[] { ... }, and if T for this call is U, then it will create an array of the upper bound of U; so if U is unbounded, this would be new Object[] { ... }. It will then produce a warning to indicate that this is unsafe.
The reason why this warning is necessary is that the runtime type of the varargs array object received in the method is not compatible with the parameter's declared type (this is called "heap pollution"). In the case where the expected type is an array of a parameterized type (e.g. List<String>[]) and it creates an array of the raw type (e.g. List[]), the difference is mostly not a big deal (although there are still some reasons why a warning is a good idea that I won't go into), and in fact people often use new List[...] as a workaround when needing to create an array of a parameterized type. However, in the case where the expected type is an array of a type variable type (e.g. U[]), creating an array of the upper bound type (e.g. Object[]) is potentially a big problem, because if this is returned to a scope which expects U to be a specific type (e.g. String), then it will expect U[] to be String[], and an array of runtime type Object[] will not be able to be put into that type since String[] and Object[] are actually separate classes at runtime, and Object[] is not a subclass of String[].
For most varargs methods that take T[], they just iterate through the varargs arguments, and they just care that the elements are of type T, and don't care that the runtime type of the varargs array object is T[]. In such cases, Java provides a #SafeVarargs annotation that can be put on the method to suppress this warning. However, in varargs methods that do care about the runtime type of the varargs array object, you should not use this annotation, because it suppresses the warning that warns of a real problem.
#Holger's answer, which is probably what the book's author was thinking of, takes advantage of varargs' implicit array creation. Even in the case of 0 variable arguments, the compiler still implicitly creates an array of the right type, and the array carries runtime information about its component type, which the method can try to use to create the array to return. So in the case of Arrays.<List<String>>construct(10), even though it doesn't look like you are passing any information in at runtime about the component type of the array, you actually are because the compiler implicitly passes a new List[] {}, and this carries the type List into the method at runtime. (Again, there is a difference between List and List<String>, but the difference is minor, and in most cases, you are not going to run into problems with this.) But this only works because the component type (List) is known at compile time, so the compiler can hard-code it at compile-time.
If you instead call this method where the component type is not known at compile time, i.e. is a type variable like Arrays.<U>construct(10), then the only thing that the compiler can send into it is new Object[], i.e. the only type information that can be passed into the method at runtime is the upper bound of the type variable (because that is known at compile time), but not the actual type that the type variable represents. The method can then only create an array with runtime type of Object[], which is incompatible with the right type of U[] (unless U happens to be Object). As mentioned above, the compiler usually generates a warning when this happens, but the book suggests you to use #SafeVarargs to suppress this warning, so you wouldn't get a warning. (And #SafeVarargs is not suitable for such a method, because it does use the runtime type of the varargs array object to create its new array.)
We can extend this further to create a proof by contradiction that the method that the question asks for is impossible in Java. We assume that there does exist a way to write the method, and assume that we have the working implementation of construct(), that is annotated with #SafeVarargs. Then we can use it to write this method:
public static <U> U[] foo(int size) {
return construct(int);
}
This method takes a size, and returns an array of the desired component type with that size, without needing to be passed any information at runtime about what that component type is. This method does not use varargs, so there is no varargs shenanigans like implicit arguments under the hood. This is just a plain non-varargs method that just takes one parameter, the size. It compiles no warnings (since construct() is annotated with #SafeVarargs). It is very obvious that this method is impossible in Java. To create an array in Java, we need to provide the component type at runtime, and this method purports to magically be able to create an array of any type you want, without you telling it what type it is that you want at runtime. This is basically creation of an array of type variable type (i.e. new U[...]), and we know that this is not possible. Since this is a contradiction, that proves that the assumption that there exists a way to write the construct() method, is false.
If you limit the circumstances that the method has to work in to only those where the component type is a reifiable type or parameterized type, and not a type variable type, then #Holger's answer would qualify. But 1) that was not stipulated in the question from the book, and 2) such a restriction would go against the whole spirit of generics, since a generic method should work just as well no matter if it's called with T being a concrete type or type variable -- it shouldn't care at all.
This is heapifying the array question and
this is my constructor of the heapcls which extends to Comparable
heapcls(T[] arr,boolean flag){
this(flag);
for (T item : arr) {
this.data.add(item);
}
for(int i=this.data.size()-1;i>=0;i--){
this.downheapify(i);
}
}
public static void main(String[] args) {
int[]arr={7,8,9,5,11,3,10,1,6,2,4,12,0,-1,13};
heapcls<Integer> client=`enter code here`new heapcls<>(arr,false);
}
this statement is giving ," Cannot infer type arguments error"
how should I change my CompareTo(by default function) to rectify the error.
since i havent overridden the bydefault CompareTo function.pls guide me.
The actual issue is that you're trying to pass an int[] to a constructor which has T[] as a parameter. Since the type on the left-hand side of the assignment is heapcls<Integer>, the constructor is supposed to have Integer[] passed to it as an argument.
Type arguments to generic types can't be primitive types and arrays of primitives aren't autoboxed. (Autoboxing an array would require allocating an entire new array.)
It seems like you could fix this just by using Integer[] instead of int[].
I'm not really sure why we get the "cannot infer type arguments" message in cases like this. I think it's a bug with the Java compiler when using the diamond type (i.e. <>) and the arguments to the constructor aren't applicable to any of the declared constructors. (Here's a much more minimal example showing the same issue with the error message: http://ideone.com/wJx16i.)
In any case, your code shouldn't compile, but because you're trying to pass an int[] as an argument to a constructor which effectively has Integer[] as a parameter, not really because the type arguments can't be inferred.
As a side note, class names in Java start with an upper-case character by convention, so your heapcls should be HeapCls (and probably just something like Heap).
I came across this section of commented code on StackOverflow. It will throw a Runtime exception. The second part (the un-commented section) will work. I am not able to find the difference between the two, as both methods are returning arrays of Objects. Maybe I am missing some basic understanding. Can you please help me?
public class Safevarargs {
/*
static <Object> Object[] asArray(Object... args) {
return args;
}
static <Object> Object[] arrayOfTwo( Object a, Object b) {
return asArray(a, b);
}
public static void main(String[] args) {
String[] bar = arrayOfTwo("hi", "mom");
}
*/
static <Object> Object[] display(Object... args) {
return args;
}
public static void main(String[] args) {
String[] str = display("hi", "mom");
System.out.print(str[0]);
}
}
I believe you are having a problem with casting. When you use the part
static <Object> Object[] display(Object... args)
what the compiler is actually seeing is
static <Object> Object[] display(Object[]... args)
then it further converts it to this
static <Object> Object[] display(List[]... args)
This might allow values that shouldn't be in there
lets say you make an object array of string and try adding an int to it. This would causes a problem with the different types. You might want to rework it without using the generic Object.
Alternatively you could use the #SafeVarargs tag.
To explain this issue, we just need to know how the generic work in java. the compiler uses the generic type information internally during the process of compilation, generating type-related errors as it processes the sources. Then, once the validation is done, the compiler generates type-erased byte code, with all references to generic types replaced with their respective type erasure.
in your second example, the it is compile to string during the compile time. but in your first example, since the asArray is called by another generic method, it will be compile into Object during compile time. As a result, in the running time, the Object[] can not be cast to String[] which will throw class cast exception. hope this can help you
This is an interesting situation here. In the first instance, Java complains about casting from an object array to a string array.
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
In the second case, it handles it just fine. The issue seems to be type erasure. When asArray returns, it's returning an array typed based on its parameters. That information is erased within the context of arrayOfTwo, where the type simply becomes Object[], and the fact that the contents are strings is no longer available.
Have a look at this post on type erasure and non-reifiable types, and the implications on varargs. It does provide some context on the warning which the code generates, about "possible heap pollution":
Safevarargs.java:11: warning: [unchecked] Possible heap pollution from parameterized vararg type Object
static Object[] display(Object... args) {
^
where Object is a type-variable:
Object extends java.lang.Object declared in method display(Object...)
From the documentation:
Heap pollution occurs when a variable of a parameterized type refers to an object that is not of that parameterized type.
I am using IntelliJ IDEA with javac on JDK 1.8. I have the following code:
class Test<T extends Throwable>
{
#SafeVarargs
final void varargsMethod( Collection<T>... varargs )
{
arrayMethod( varargs );
}
void arrayMethod( Collection<T>[] args )
{
}
}
IntelliJ IDEA does not highlight anything in the above code as a warning. However, when compiling, the following line appears in the "Make" tab of the "Messages" view:
Warning:(L, C) java: Varargs method could cause heap pollution from
non-reifiable varargs parameter varargs
Note #1: I have already specified #SafeVarargs.
Note #2: Warning:(L,C) points to varargs being passed as an argument to arrayMethod()
Assuming that I know what I am doing, and supposing that I am pretty sure that there will be no heap pollution or that I promise that I will not call this method in some funky way which might result in heap pollution, what do I need to do to suppress this warning message?
NOTE: There is a multitude of questions on stackoverflow regarding varargs methods, but it appears that there is none that addresses this specific problem. As a matter of fact, the entire interwebz appear to be rather poor in answers to this particular question.
None of the answers I've seen on this question seem to me to be satisfactory so I thought I'd take a stab at it.
Here's the way I see it:
#SafeVarargs
Suppresses the warning: [unchecked] Possible heap pollution from parameterized vararg type Foo.
Is part of the method's contract, hence why the annotation has runtime retention.
Is a promise to the caller of the method that the method will not mess up the heap using the generic varargs argument.
#SuppressWarnings("varargs")
Suppresses the warning: [varargs] Varargs method could cause heap pollution from non-reifiable varargs parameter bar.
Is a cure for problems occurring within the method code, not on the method's contract, hence why the annotation only has source code retention.
Tells the compiler that it doesn't need to worry about a callee method called by the method code messing up the heap using the array resulting from the non-reifiable varargs parameter.
So if I take the following simple variation on OP's original code:
class Foo {
static <T> void bar(final T... barArgs) {
baz(barArgs);
}
static <T> void baz(final T[] bazArgs) { }
}
The output of $ javac -Xlint:all Foo.java using the Java 9.0.1 compiler is:
Foo.java:2: warning: [unchecked] Possible heap pollution from parameterized vararg type T
static <T> void bar(final T... barArgs) {
^
where T is a type-variable:
T extends Object declared in method <T>bar(T...)
1 warning
I can make that warning go away by tagging bar() as #SafeVarargs. This both makes the warning go away and, by adding varargs safety to the method contract, makes sure that anyone who calls bar will not have to suppress any varargs warnings.
However, it also makes the Java compiler look more carefully at the method code itself - I guess in order to verify the easy cases where bar() might be violating the contract I just made with #SafeVarargs. And it sees that bar() invokes baz() passing in barArgs and figures since baz() takes an Object[] due to type erasure, baz() could mess up the heap, thus causing bar() to do it transitively.
So I need to also add #SuppressWarnings("varargs") to bar() to make that warning about bar()'s code go away.
In fact you should not write your code in this way. Consider the following example:
import java.util.*;
class Test<T extends Throwable>
{
#SafeVarargs
#SuppressWarnings("varargs")
final void varargsMethod( Collection<T>... varargs )
{
arrayMethod( varargs );
}
void arrayMethod( Collection<T>[] args )
{
Object[] array = args;
array[1] = new Integer(1);
//
//ArrayList<Integer> list = new ArrayList<>();
//list.add(new Integer(1));
//array[1] = list;
}
public static void main(String[] args)
{
ArrayList<Exception> list1 = new ArrayList<>();
ArrayList<Exception> list2 = new ArrayList<>();
(new Test<Exception>()).varargsMethod(list1, list2);
}
}
If you run the code, you will see an ArrayStoreException because you put an Integer into a Collection<T> array.
However, if you replace array[1] = new Integer(1); with the three comment lines (i.e. to put an ArrayList<Integer> into the array), due to type erasure, no exception is thrown and no compilation error occurs.
You want to have a Collection<Exception> array, but now it contains a ArrayList<Integer>. This is quite dangerous as you won't realise there is a problem.
An additional (and quite superfluous-looking) #SuppressWarnings( "varargs" ) is needed to suppress the warning, as follows:
#SafeVarargs
#SuppressWarnings( "varargs" )
final void varargsMethod( Collection<T>... varargs )
{
arrayMethod( varargs );
}
Assuming that I know what I am doing
I refuse to assume this since this seems incredibly wrong.
The situation you're running into here is that, because generics are not reifiable, you're declaring a generic array, which is generally frowned upon. Generics and arrays don't mix very well, given that an array is covariant (String[] is an Object[] in the same way that a String is an Object), whereas generics are invariant (List<String> is not a List<Object>, even though a String is an Object).
If you want a collection of collections...just pass one. It's safer than mingling arrays and generics.
final void varargsMethod(Collection<<Collection<? super T>> collections) { }
This might explain the reason why. I just copied it from Effective Java 2nd Edition, Item 25. Hopefully, it can help.
The prohibition on generic array creation can be annoying. It means, for example, that it’s not generally possible for a generic type to return an array of its element type (but see Item 29 for a partial solution). It also means that you can get confusing warnings when using varargs methods (Item 42) in combination with generic types. This is because every time you invoke a varargs method, an array is created to hold the varargs parameters. If the element type of this array is not reifiable, you get a warning. There is little you can do about these warnings other than to suppress them (Item 24), and to avoid mixing generics and varargs in your APIs.
I have the following code:
private double doThings(List<ClassA> list){
ClassB[] array = list.toArray(new ClassB[0]);
// ...
}
This compiles. Why?
Based on javadoc:
ArrayStoreException - if the runtime type of the specified array is not a supertype of the runtime type of every element in this list.
java.util.List source code is interesting:
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/List.java
The class is generic by the type :
public interface List<E> extends Collection<E>
However, toArray method is parametrized by a different type :
<T> T[] toArray(T[] a);
This is a generic method using a different type as a parameter than the class itself. There's no link between T and E. That's why you can use different types for the argument in toArray method than the class that was used to parametrize the list.
It would be interesting to find the reasoning behind this design. On first look, it's a bit strange.
There is no way the method can be declared that will prevent the possibility of ArrayStoreException. Even if the method were declared to have parameter type A[], you can still pass it an array whose runtime type is SubClassOfA[], and if the list has A objects, then it will cause an ArrayStoreException because they cannot be stored in a SubClassOfA[].
Since there is no way to avoid it, and it depends on the programmer to use it properly, they might as well make the method as general as possible, and take any type of array. When used correctly (i.e. when we know that all objects are also instances of B), it will not cause an ArrayStoreException.