Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to solve the "A generic array of T is created for a varargs parameter" compiler warning?

Tags:

java

generics

People also ask

Why generic arrays are not allowed?

If generic array creation were legal, then compiler generated casts would correct the program at compile time but it can fail at runtime, which violates the core fundamental system of generic types.

Can we use generic with array?

Java allows generic classes, methods, etc. that can be declared independent of types. However, Java does not allow the array to be generic. The reason for this is that in Java, arrays contain information related to their components and this information is used to allocate memory at runtime.

What type is Varargs?

Varargs is a short name for variable arguments. In Java, an argument of a method can accept arbitrary number of values.


In Java 6, other than adding @SuppressWarnings("unchecked"), I don't think so.

This bug report has more information but it boils down to the compiler not liking arrays of generic types.


In Java 7, annotate the method declaration with @SafeVarargs


If you're after a fluent-type interface, you could try the builder pattern. Not as concise as varargs but it is type safe.

A static generically-typed method can eliminate some of the boilerplate when using the builder, while retaining the type safety.

The builder

public class ArgBuilder<T> implements Iterable<T> {

    private final List<T> args = new ArrayList<T>();

    public ArgBuilder<T> and(T arg) {
        args.add(arg);
        return this;
    }

    @Override
    public Iterator<T> iterator() {
        return args.iterator();
    }

    public static <T> ArgBuilder<T> with(T firstArgument) {
        return new ArgBuilder<T>().and(firstArgument);
    }
}

Using it

import static com.example.ArgBuilder.*;

public class VarargsTest {

    public static void main(String[] args) {
        doSomething(new ArgBuilder<String>().and("foo").and("bar").and("baz"));
        // or
        doSomething(with("foo").and("bar").and("baz"));
    }

    static void doSomething(Iterable<String> args) {
        for (String arg : args) {
            System.out.println(arg);
        }
    }
}

Explicitly casting parameters to Object in vararg method invocation will make the compiler happy without resorting to @SuppressWarnings.

public static <T> List<T> list( final T... items )
{
    return Arrays.asList( items );
}

// This will produce a warning.
list( "1", 2, new BigDecimal( "3.5" ) )

// This will not produce a warning.
list( (Object) "1", (Object) 2, (Object) new BigDecimal( "3.5" ) )

// This will not produce a warning either. Casting just the first parameter to 
// Object appears to be sufficient.
list( (Object) "1", 2, new BigDecimal( "3.5" ) )

I believe the issue here is that the compiler needs to figure out what concrete type of array to create. If the method is not generic, the compiler can use type information from the method. If the method is generic, it tries to figure out the array type based on parameters used at invocation. If the parameter types are homogenic, that task is easy. If they vary, the compiler tries to be too clever in my opinion and creates a union-type generic array. Then it feels compelled to warn you about it. A simpler solution would have been to create Object[] when type cannot be better narrowed down. The above solution forces just that.

To understand this better, play around with invocations to the above list method compared to the following list2 method.

public static List<Object> list2( final Object... items )
{
    return Arrays.asList( items );
}

It is a very easy problem to solve: Use List<T>!

Arrays of reference type should be avoided.

In the current version of Java (1.7), you can mark method with @SafeVargs which will remove the warning from the caller. Careful with that though, and you're still better off without legacy arrays.

List.of() provides a relatively concise way of writing an (unmodifiable) List until Java gains an appropriate literal representation.

See also the Improved Compiler Warnings and Errors When Using Non-Reifiable Formal Parameters with Varargs Methods tech note.


You can add @SafeVarargs to method since Java 7, and you don't have to annotate on client code.

class Assembler<X, Y> {

    @SafeVarargs
    final void assemble(X container, Y... args) {
        //has to be final...
    }
}