web-dev-qa-db-fra.com

Est-il possible de résoudre le message "Un tableau générique de T est créé pour un paramètre varargs"?

Il s'agit d'une version simplifiée du code en question. Une classe générique utilise une autre classe avec des paramètres de type générique et doit transmettre l'un des types génériques à une méthode avec des paramètres 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-t-il un moyen correct de transmettre le paramètre générique à une méthode varargs sans rencontrer cet avertissement?

Bien sûr, quelque chose comme

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

ne fonctionne pas car vous ne pouvez pas créer de tableaux génériques.

148
matt b

À part l'ajout de @SuppressWarnings("unchecked"), je ne le pense pas.

Ce rapport de bogue contient plus d'informations, mais le compilateur n'aime pas les tableaux de types génériques.

85
Kevin

Tom Hawtin l'a souligné dans un commentaire, mais pour être plus explicite: oui, vous pouvez le résoudre sur le site de déclaration (plutôt que sur les sites d'appels (potentiellement nombreux)): basculez vers JDK7.

Comme vous pouvez le voir dans article de Joseph Darcy sur le blog , l'exercice Project Coin a permis de sélectionner de petites améliorations linguistiques supplémentaires pour Java 7 accepté proposition de Bob Lee = permettre à quelque chose comme @SuppressWarnings("varargs") du côté de la méthode de faire en sorte que cet avertissement disparaisse dans les situations où il était connu d'être sûr.

Cela a été implémenté dans OpenJDK avec this commit .

Cela peut ou peut ne pas être utile à votre projet (beaucoup de gens ne seraient pas heureux de passer à une version instable de la JVM avant la publication!), Mais c'est peut-être le cas - ou peut-être quelqu'un qui trouvera cette question plus tard (après la sortie de JDK7 ) le trouvera utile.

57
Cowan

Si vous recherchez une interface de type courant, vous pouvez essayer le modèle de générateur. Pas aussi concis que varargs mais le type est sûr.

Une méthode statique de type générique peut éliminer une partie du passe-partout lors de l'utilisation du générateur, tout en préservant la sécurité du type.

Le constructeur

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

tilisation

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

Le transfert explicite de paramètres vers Object dans l’appel de la méthode vararg satisfera le compilateur sans recourir à @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" ) )

Je pense que le problème ici est que le compilateur doit déterminer quel type de tableau concret créer. Si la méthode n'est pas générique, le compilateur peut utiliser les informations de type de la méthode. Si la méthode est générique, elle essaie de déterminer le type de tableau en fonction des paramètres utilisés lors de l'appel. Si les types de paramètres sont homogènes, cette tâche est facile. S'ils varient, le compilateur essaie, à mon avis, d'être trop intelligent et crée un tableau générique de type union. Ensuite, il se sent obligé de vous en avertir. Une solution plus simple aurait été de créer Object [] lorsque le type ne peut pas être mieux défini. La solution ci-dessus force justement cela.

Pour mieux comprendre cela, jouez avec les invocations de la méthode list ci-dessus par rapport à la méthode list2 suivante.

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

Vous pouvez ajouter @ SafeVarargs à la méthode depuis Java 7, et vous n'avez pas à annoter le code client.

class Assembler<X, Y> {

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

C'est un problème très facile à résoudre: Utilisez List<T>!

Les tableaux de type référence doivent être évités.

Dans la version actuelle de Java (1.7), vous pouvez marquer une méthode avec @SafeVargs qui supprimera l’avertissement de l’appelant. Attention cependant, et vous êtes toujours mieux sans les tableaux hérités.

Voir également le Avertissements et erreurs du compilateur améliorés lors de l'utilisation de paramètres formels non réiférables avec les méthodes Varargs note technique.

1

Vous pouvez avoir surcharger les méthodes. Cela ne résout pas votre problème, mais minimise le nombre d’avertissements (et oui, c’est 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) { ... }
}
1
EA.

Lorsque je travaille avec des tableaux de type générique, je suis obligé de transmettre une référence au type générique. Avec cela, je peux réellement faire le code générique, en utilisant Java.lang.reflect.Array.

http://Java.Sun.com/javase/6/docs/api/Java/lang/reflect/Array.html

0
KLE