É possível resolver o aviso do compilador "Uma matriz genérica de T é criada para um parâmetro varargs"?

153

Esta é uma versão simplificada do código em questão, uma classe genérica usa outra classe com parâmetros de tipo genérico e precisa passar um dos tipos genéricos para um método com parâmetros varargs:

class Assembler<X, Y> {
    void assemble(X container, Y... args) { ... }
}

class Component<T> {
    void useAssembler(T something) {

        Assembler<String, T> assembler = new Assembler<String, T>();

        //generates warning:
        // Type safety : A generic array of T is
        // created for a varargs parameter
        assembler.assemble("hello", something);
    }

}

Existe alguma maneira correta de passar o parâmetro genérico para um método varargs sem encontrar esse aviso?

Claro que algo como

assembler.assemble("hello", new T[] { something });

não funciona, pois você não pode criar matrizes genéricas.

matt b
fonte
3
Estranho. Parece que o compilador deve ser capaz de garantir a segurança do tipo completo aqui.
21411
3
Entrada relacionada nas Perguntas frequentes sobre genéricos de Java de Angelika Langer: angelikalanger.com/GenericsFAQ/FAQSections/…
Flow

Respostas:

88

Além de adicionar @SuppressWarnings("unchecked"), acho que não.

Este relatório de erros tem mais informações, mas resume-se ao compilador que não gosta de matrizes de tipos genéricos.

Kevin
fonte
3
Esqueci de mencionar que queria evitar o @SuppressWarnings ("desmarcado"). Esse relatório de erro me dá pouca esperança!
mate b
3
Como Joshua Bloch coloca em Java eficaz: "Não misture genéricos e matrizes".
Timmos
20
então, implicitamente: não use Varargs com Generics! Certo ... a decisão de mapear varargs para Array e não Collection continuará picando o java para sempre. Muito bem, Sr. Gosling.
22414 bernstein
57

Tom Hawtin apontou isso em um comentário, mas para ser mais explícito: sim, você pode resolver isso no site da declaração (em vez dos (potencialmente muitos) sites de chamada): alterne para o JDK7.

Como você pode ver na postagem do blog de Joseph Darcy , o projeto Project Coin para selecionar algumas pequenas melhorias incrementais de linguagem para o Java 7 aceitou a proposta de Bob Lee de permitir que algo como @SuppressWarnings("varargs")no lado do método faça esse aviso desaparecer em situações em que era conhecido seguro.

Isso foi implementado no OpenJDK com esse commit .

Isso pode ou não ser útil ao seu projeto (muitas pessoas não ficariam felizes em mudar para uma versão instável da JVM antes do lançamento!), Mas talvez seja - ou talvez alguém que encontre essa pergunta mais tarde (depois que o JDK7 for lançado) ) achará útil.

Cowan
fonte
7
O recurso Projeto Coin mencionado está agora disponível - veja @SafeVarargs em Java 7.
George Hawkins
A alternativa E na proposta de Bob é atraente.
Christopher Perry
Java 8 parece usar @SafeVarargs vez de @SuppressWarnings ( "varargs")
Paul Wintz
17

Se você procura uma interface do tipo fluente, pode experimentar o padrão do construtor. Não é tão conciso quanto o varargs, mas é do tipo seguro.

Um método estático de tipo genérico pode eliminar parte do padrão ao usar o construtor, mantendo a segurança do tipo.

O construtor

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);
    }
}

Usando isso

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);
        }
    }
}
npgall
fonte
1
O poder da composição. Eu gosto muito mais do que varargs, é mais expressivo.
Christopher Perry
1
@ChristopherPerry, você também deve considerar sua base de códigos. O subjacente Collection(neste caso, um ArrayList) é imposto ao chamador, enquanto eles podem saber que a LinkedListé mais apropriado ou uma matriz imutável (como as variáveis ​​da pergunta OP). Em um caso de uso não especializado, isso pode ser apropriado, mas apenas apontando que isso também é uma limitação, de certa forma, dependendo do código que envolve isso e suas necessidades.
searchengine27
5

A conversão explícita de parâmetros para Object na invocação do método vararg fará o compilador feliz sem recorrer a @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" ) )

Acredito que a questão aqui é que o compilador precisa descobrir que tipo concreto de matriz criar. Se o método não for genérico, o compilador pode usar informações de tipo do método. Se o método for genérico, ele tenta descobrir o tipo de matriz com base nos parâmetros usados ​​na chamada. Se os tipos de parâmetros forem homogêneos, essa tarefa será fácil. Se eles variarem, o compilador tenta ser muito inteligente na minha opinião e cria uma matriz genérica do tipo união. Então ele se sente obrigado a avisá-lo sobre isso. Uma solução mais simples seria criar Object [] quando o tipo não puder ser melhor refinado. A solução acima força exatamente isso.

Para entender isso melhor, brinque com invocações para o método list acima, comparado ao método list2 a seguir.

public static List<Object> list2( final Object... items )
{
    return Arrays.asList( items );
}
Konstantin Komissarchik
fonte
Isso funciona, também por exemplo: Iterator <?> It = Arrays.asList ((Object) t) .iterator; if (if, hasNext ()) {class = it.next (). getClass (); } por exemplo, para obter a classe de um objeto de uma matriz de tipo desconhecido.
ggb667
2

Você pode adicionar @SafeVarargs ao método desde o Java 7 e não precisa anotar no código do cliente.

class Assembler<X, Y> {

    @SafeVarargs
    final void assemble(X container, Y... args) {
        //has to be final...
    }
}
Daniel Hári
fonte
1

Você pode sobrecarregar os métodos. Isso não resolve o seu problema, mas minimiza o número de avisos (e sim, é um hack!)

class Assembler<X, Y> {
  void assemble(X container, Y a1) { ... }
  void assemble(X container, Y a1, Y a2) { ... }
  void assemble(X container, Y a1, Y a2, Y a3) { ... }
  void assemble(X container, Y a1, Y a2, Y a3, Y a4) { ... }
  void assemble(X container, Y... args) { ... }
}
EA.
fonte
23
Ai credo. Esse é exatamente o tipo de invasão que os varargs devem impedir.
22611 Amanda S
1
Essa pode ser uma abordagem válida, por exemplo, dê uma olhada no ImmutableSet.of do Guava .
9133 Jonathan
1

É um problema muito fácil de resolver: Use List<T>!

Matrizes do tipo de referência devem ser evitadas.

Na versão atual do Java (1.7), você pode marcar o método com o @SafeVargsqual removerá o aviso do chamador. Cuidado com isso, porém, e você ainda está melhor sem matrizes herdadas.

Consulte também as notas técnicas e avisos aprimorados do compilador ao usar parâmetros formais não reificáveis ​​com a Varargs Methods .

Tom Hawtin - linha de orientação
fonte
6
isso é inevitável com um parâmetro varargs, não é?
mate b
4
Existe uma proposta para o JDK7 para permitir que a supressão de aviso continue na declaração do método varargs em vez de seu uso.
Tom Hawtin - tackline
11
Isso ignora completamente um aspecto importante da pergunta do autor - os parâmetros varargs criam uma matriz e isso gera esse aviso.
precisa saber é o seguinte
2
Estou de acordo com @Tom Hawtin - linha de orientação. Para detalhes, consulte Bloch << Java Efetivo >> Item 25: Preferir listas a matrizes.
Stan Kurilin
2
Eu geralmente concordar com Bloch em um presente, mas VarArgs é uma clara exceção à regra ...
Joeri Hendrickx
0

Quando trabalhos com matrizes do tipo genérico, sou forçado a passar uma referência ao tipo genérico. Com isso, eu posso realmente fazer o código genérico, usando java.lang.reflect.Array.

http://java.sun.com/javase/6/docs/api/java/lang/reflect/Array.html

KLE
fonte
Não estou trabalhando com matrizes do tipo genérico, mas não diretamente, apenas varargs de um tipo genérico.
matt b