Reputation: 3773
A puzzle from this blog. Similar to SO1445233.
Given the following source listing, explain why the compiler is producing a warning at invocation to the list method and give a solution for removing the warning without resorting to @SuppressWarnings annotation.
public class JavaLanguagePuzzle3 {
public static void main(String[] args) {
list("1", 2, new BigDecimal("3.5"));
}
private static <T> List<T> list(T... items) {
return Arrays.asList(items);
}
}
Warning:
Type safety: A generic array of Object&Serializable&Comparable<?> is created for a varargs parameter
Upvotes: 5
Views: 4903
Reputation: 44808
I asked a question on this a while ago.
Problem: Given the method header <T extends List<?>> void foo(T... args)
You can store non-T values in the generated array (unsafe behavior). (See my question below for more details)
Solution: In Java 7 they added a @SafeVarargs annotation you can put on your method to suppress that warning.
Simplified Varargs Method Invocation in Java 7
Upvotes: 1
Reputation: 2669
Change the body of main from
list("1", 2, new BigDecimal("3.5"));
to
JavaLanguagePuzzle3.<Object>list("1", 2, new BigDecimal("3.5"));
Reason: the <>
syntax specifies which version of the generic method you want. But, you need to put .
in front of it to make the parser happy, and the class name before that for the same reason.
Source: http://codeidol.com/java/javagenerics/Introduction/Generic-Methods-and-Varargs/
Upvotes: 3
Reputation: 6181
The error message is actually slightly misleading. The issue here is not that the type argument is generic, it's that it's an intersection type. Only one element type of that intersection type can be reified as the component type of the array. Thus storing objects into that array which do implement some of the other element types of the intersection type will not raise an ArrayStoreException
:
public class Example {
interface A {}
interface B {}
interface C {}
static class X implements A, B {}
static class Y implements A, B, C {}
public static void main(String[] args) {
// inferred type: A&B, erased down to A resulting in a A[]
bar(new X(), new Y());
}
static <T> void bar(T... ts) {
Object[] os = ts;
os[0] = new A() {}; // should fail, does not => warning
os[0] = new B() {}; // fails as expected
}
}
Upvotes: 0
Reputation: 3773
Here's my thoughts.
public static interface Foo extends Serializable, Comparable<Object> {
}
public static void main(String[] args) {
// Problem: Unsafe: varargs has generic type
implicitList("1", 2, BigDecimal.valueOf(3.5)); // warning: generic vararg
// Solution 1: Constrain type of varags explicitly through generics
explicitList1(Object.class, "1", 2, BigDecimal.valueOf(3.5));
// However, we could still have the same error from problem
explicitList1(Foo.class, "1", 2, BigDecimal.valueOf(3.5)); // warning: generic vararg
// Fix: Make containing class to exact type (PECS) an array is both producer and consumer
explicitList2(Foo.class, "1", 2, BigDecimal.valueOf(3.5)); // error: incompatible args
// Solution 2: Override varargs by passing array
implicitList(new Object[] { "1", 2, BigDecimal.valueOf(3.5) });
}
private static <T> List<T> explicitList1(Class<? extends T> klass, T... items) {
return Arrays.asList(items);
}
private static <T> List<T> explicitList2(Class<T> klass, T... items) {
return Arrays.asList(items);
}
private static <T> List<T> implicitList(T... items) {
return Arrays.asList(items);
}
Upvotes: 2
Reputation: 234795
The message is because there's a mixture of types as the args to list
and the correct binding for the type parameter T
isn't obvious. One fix (not in the spirit of type safety) is to remove the generics:
private static List<Object> list(Object... items) {
return Arrays.asList(items);
}
Upvotes: 0