È possibile risolvere l'avviso del compilatore "Viene creato un array generico di T per un parametro varargs"?


153

Questa è una versione semplificata del codice in questione, una classe generica utilizza un'altra classe con parametri di tipo generico e deve passare uno dei tipi generici a un metodo con parametri 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);
    }

}

Esiste un modo corretto per passare il parametro generico a un metodo varargs senza incontrare questo avviso?

Certo qualcosa del genere

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

non funziona poiché non è possibile creare matrici generiche.


3
Strano. Sembra che il compilatore dovrebbe essere in grado di garantire la piena sicurezza del tipo qui.
Erickson,

3
Flow

Risposte:


88

A parte l'aggiunta @SuppressWarnings("unchecked"), non credo.

Questa segnalazione di bug ha più informazioni ma si riduce al compilatore non gradendo le matrici di tipi generici.


3
Ho dimenticato di menzionare che volevo evitare @SuppressWarnings ("deselezionato"). Quella segnalazione di bug mi dà poche speranze!
opaco b

3
Come dice Joshua Bloch in Effective Java: "Non mescolare generici e array".
Timmos,

20
quindi, implicitamente: non utilizzare Varargs con Generics! Giusto ... la decisione di mappare varargs su Array e non su Collection continuerà a pungere java per sempre. Ben fatto Mr. Gosling.
Bernstein,

57

Tom Hawtin lo ha sottolineato in un commento, ma per essere più espliciti: sì, puoi risolverlo sul sito della dichiarazione (piuttosto che sui (potenzialmente molti) siti di chiamata): passa a JDK7.

Come puoi vedere nel post sul blog di Joseph Darcy , l'esercizio di Project Coin per selezionare alcuni piccoli miglioramenti del linguaggio incrementale per Java 7 ha accettato la proposta di Bob Lee di consentire a qualcosa come @SuppressWarnings("varargs")sul lato del metodo di far sparire questo avviso in situazioni in cui era noto sicuro.

Questo è stato implementato in OpenJDK con questo commit .

Questo potrebbe essere o non essere utile al tuo progetto (molte persone non sarebbero felici di passare a una versione instabile pre-release di JVM!) Ma forse lo è - o forse qualcuno che trova questa domanda in seguito (dopo che JDK7 è uscito ) lo troverà utile.


7
La menzionata funzionalità Project Coin è ora disponibile - vedi @SafeVarargs in Java 7.
George Hawkins,

L'alternativa E nella proposta di Bob è allettante.
Christopher Perry,

Java 8 sembra usare @SafeVarargs invece di @SuppressWarnings ("varargs")
Paul Wintz,

17

Se stai cercando un'interfaccia di tipo fluente, potresti provare il modello del builder. Non è conciso come Varargs ma è sicuro.

Un metodo statico di tipo generico può eliminare parte della piastra della caldaia quando si utilizza il costruttore, mantenendo la sicurezza del tipo.

Il costruttore

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

Usandolo

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

1
Il potere della composizione. Mi piace molto più di Varargs, è più espressivo.
Christopher Perry,

1
@ChristopherPerry bene devi considerare anche la tua base di codice. Il sottostante Collection(in questo caso un ArrayList) è forzato sul chiamante, mentre possono sapere che a LinkedListè più appropriato, o un array immutabile stesso (come i varargs della domanda OP). In un caso d'uso non specializzato questo può essere appropriato, ma sottolineando che questa è anche una limitazione, in un certo senso, a seconda del codice che circonda questo e delle tue esigenze.
searchengine27,

5

Il cast esplicito di parametri su Object nell'invocazione del metodo vararg renderà felice il compilatore senza ricorrere 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" ) )

Credo che il problema qui sia che il compilatore deve capire quale tipo concreto di array creare. Se il metodo non è generico, il compilatore può utilizzare le informazioni sul tipo dal metodo. Se il metodo è generico, prova a capire il tipo di array in base ai parametri utilizzati durante l'invocazione. Se i tipi di parametro sono omogenei, tale compito è semplice. Se variano, il compilatore cerca di essere troppo intelligente secondo me e crea un array generico di tipo unione. Quindi si sente costretto ad avvertirti a riguardo. Una soluzione più semplice sarebbe stata quella di creare Object [] quando non è possibile restringere meglio il tipo. La soluzione di cui sopra forza proprio questo.

Per capirlo meglio, gioca con le invocazioni al metodo elenco sopra riportato rispetto al seguente metodo elenco2.

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

Funziona anche ad esempio: Iterator <?> It = Arrays.asList ((Object) t) .iterator; if (if, hasNext ()) {class = it.next (). getClass (); } ad esempio per estrarre la classe di un oggetto da una matrice di tipo sconosciuto.
ggb667,

2

È possibile aggiungere @SafeVarargs al metodo da Java 7 e non è necessario annotare sul codice client.

class Assembler<X, Y> {

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

1

Puoi avere un sovraccarico dei metodi. Questo non risolve il tuo problema ma minimizza il numero di avvisi (e sì, è un 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) { ... }
}

23
Ew. Questo è esattamente il tipo di hack che i vararg dovrebbero prevenire.
Amanda S,

1
Questo può essere un valido approccio, per esempio, dare un'occhiata al di Guava ImmutableSet.of .
Jonathan,

1

È un problema molto semplice da risolvere: utilizzare List<T>!

Le matrici di tipo di riferimento devono essere evitate.

Nella versione corrente di Java (1.7), è possibile contrassegnare il metodo con @SafeVargscui rimuoverà l'avviso dal chiamante. Attenzione però, e stai ancora meglio senza array legacy.

Vedere anche Avvertenze ed errori del compilatore migliorati quando si utilizzano parametri formali non riutilizzabili con i metodi Varargs nota tecnica.


6
questo è inevitabile con un parametro varargs, non è vero?
matt b

4
Esiste una proposta per JDK7 di consentire alla soppressione degli avvisi di andare sulla dichiarazione del metodo varargs piuttosto che sul suo utilizzo.
Tom Hawtin - tackline

11
Ciò ignora completamente un aspetto importante della domanda dell'autore: i parametri varargs creano un array e questo genera questo avviso.
Daniel Yankowsky,

2
Sono d'accordo con @Tom Hawtin - tackline. Per i dettagli, consultare Bloch << Effecive Java >> Item 25: Preferisci gli elenchi agli array.
Stan Kurilin,

2
Sono generalmente d'accordo con Bloch su questo, ma varargs è una chiara eccezione alla regola ...
Joeri Hendrickx,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.