55 votes

Référence constructeur - aucun avertissement lors de la création d'un tableau de génériques

En Java, il n'est pas possible de créer directement un tableau de type générique:

 Test<String>[] t1 = new Test<String>[10]; // Compile-time error
 

Cependant, nous pouvons le faire en utilisant un type brut:

 Test<String>[] t2 = new Test[10]; // Compile warning "unchecked"
 

En Java 8, il est également possible d’utiliser une référence de constructeur:

 interface ArrayCreator<T> {
    T create(int n);
}

ArrayCreator<Test<String>[]> ac = Test[]::new; // No warning
Test<String>[] t3 = ac.create(10);
 

Pourquoi le compilateur n'affiche pas l'avertissement dans le dernier cas? Il utilise toujours le type brut pour créer le tableau, non?

28voto

Holger Points 13789

Votre question est justifiée. En bref, la méthode de référence n'est, en effet, utiliser le raw type (ou devrait utiliser le raw type) et de la raison, le pourquoi de la création du générique de tableaux est interdit, s'applique encore lors de l'utilisation de la méthode des références, par conséquent, être en mesure d'en silence créer une fonction de création d'un générique de la matrice est clairement en contradiction avec l'intention de la conception de langage.

La raison pour laquelle la création du générique de la matrice est interdit, c'est que le type du tableau de l'héritage, à partir d'une pré-Génériques de l'époque, est incompatible avec le type générique du système. I. e. vous pouvez écrire:

IntFunction<List<String>[]> af = List[]::new; // should generate warning
List<String>[] array = af.apply(10);
Object[] objArray = array;
objArray[0] = Arrays.asList(42);
List<String> list = array[0]; // heap pollution

À cet endroit, il convient de souligner que, contrairement à certaines réponses ici, le compilateur ne pas effectuer l'inférence de type sur l'expression List[]::new de déduire l'élément générique de type List<String>. Il est facile de prouver que le générique de la création de la matrice est toujours interdit:

IntFunction<List<String>[]> af = List<String>[]::new; // does not compile

Depuis List<String>[]::new est illégale, il serait étrange si List[]::new a été acceptée sans un avertissement, en supposant qu'il soit effectivement illégale List<String>[]::new.

JLS §15.13 stipule clairement:

Si une méthode de référence l'expression est de la forme ArrayType :: new, alors ArrayType doit désigner un type qui est reifiable (§4.7), ou une erreur de compilation se produit.

Cela implique déjà qu' List<String>[]::new est illégal, car List<String> n'est pas reifiable, alors qu' List<?>[]::new est légal, List<?> est reifiable, et List[]::new est légal si l'on considère List pour une crue de type, comme la crue de type List est reifiable.

Ensuite, §15.13.1 états:

Si la méthode de référence l'expression est de la forme ArrayType :: new, un seul théorique de la méthode est considérée. La méthode a un seul paramètre de type int, renvoie la ArrayType, et n'a pas d' throws de la clause. Si n = 1, c'est le seul potentiellement applicables méthode; sinon, il n'y a pas potentiellement applicables méthodes.

En d'autres termes, le comportement de l' List[]::new expression ci-dessus est le même que si vous aviez écrit:

    IntFunction<List<String>[]> af = MyClass::create;
…
private static List[] create(int i) {
    return new List[i];
}

sauf que la méthode de create est uniquement théorique. Et en effet, avec cette méthode explicite de la déclaration, il y a seulement cru type de mises en garde à l' create méthode, mais pas décoché les avertissements concernant la conversion de List[] de List<String>[] à la méthode de référence. Il est donc compréhensible, ce qui se passe dans le compilateur dans l' List[]::new de cas, où la méthode à l'aide de matières types est seulement théorique, c'est à dire n'existe pas dans le code source.

Mais l'absence de décoché avertissements est une violation claire de JLS §5.1.9, Unchecked Conversion:

Laissez - G nom générique de la déclaration de type à n paramètres de type.

Il y a un unchecked conversion les matières de la classe ou de type d'interface (§4.8) G pour tout paramétré le type de la forme G<T₁,...,Tₙ>.

Il y a un unchecked conversion de la crue de type array G[]ᵏ pour tout type de tableau de la forme G<T₁,...,Tₙ>[]ᵏ. (La notation []ᵏ indique un type de tableau de k dimensions.)

L'utilisation d'un unchecked conversion provoque au moment de la compilation décoché avertissement , à moins que tous les arguments de type Tᵢ (1 ≤ in) sont sans limite de caractères génériques (§4.5.1), ou la décoché avertissement est supprimée par l' SuppressWarnings d'annotation (§9.6.4.5).

Ainsi, une conversion de List[] de List<?>[] est légal, List est paramétré avec une vive générique, mais la conversion de l' List[] de List<String>[] doit produire un décoché avertissement, ce qui est crucial ici, comme l'utilisation de l' List[]::new ne produit pas de la crue de type d'avertissement qui s'affiche avec un explicite la méthode de création. L'absence de crue de type d'avertissements ne semble pas être une violation (que j'ai compris §4.8) et il ne serait pas un problème, si javac créé les décoché avertissement.

10voto

Willis Blackburn Points 980

Le mieux que je peux trouver, c'est que le JLS spécifie une méthode de référence pour le constructeur d'un type générique en déduit les paramètres génériques: "Si une méthode ou d'un constructeur est générique, le type des arguments peut être déduit ou prévu expressément." Plus tard, il donne ArrayList::new comme un exemple et qu'il décrit comme "présumées des arguments de type générique de la classe", établissant ainsi qu' ArrayList::new (et pas ArrayList<>::new) est la syntaxe qui déduit des arguments.

Étant donné une classe:

public static class Test<T> {
    public Test() {}
}

cela donne un avertissement:

Test<String> = new Test(); // No <String>

mais ce n'est pas:

Supplier<Test<String>> = Test::new; // No <String> but no warning

parce qu' Test::new implicitement déduit le générique arguments.

Donc je suppose qu'une méthode de référence pour un constructeur tableau fonctionne de la même manière.

5voto

Patrick Parker Points 3366

Il utilise toujours cru type pour créer le tableau, à droite?

Java les génériques sont juste au moment de la compilation de l'illusion, de sorte que le raw type va bien sûr être utilisé lors de l'exécution, pour créer le tableau.

Pourquoi ne pas le compilateur d'affichage de l'avertissement dans le dernier cas?

Oui, la décoché fonte d' Test[] de Test<String>[] est encore en cours, c'est juste qui se passe derrière les scènes dans un contexte anonyme.

Test<String>[] t3 = ((IntFunction<Test<String>[]>) Test[]::new).apply(10);

Depuis la méthode anonyme est en train de faire le sale travail, la décoché cast efficacement disparaît dans le code managé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X