Référence constructeur – pas d’avertissement lorsque le tableau générique est créé

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

Test[] t1 = new Test[10]; // Comstack-time error 

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

 Test[] t2 = new Test[10]; // Comstack warning "unchecked" 

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

 interface ArrayCreator { T create(int n); } ArrayCreator<Test[]> ac = Test[]::new; // No warning Test[] t3 = ac.create(10); 

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

Votre question est justifiée. En bref, la référence de méthode utilise bien le type brut (ou doit utiliser le type brut) et la raison pour laquelle la création de tableaux génériques est interdite s’applique toujours lors de l’utilisation de références de méthodes, permettant ainsi de créer un tableau générique viole clairement l’intention de la conception du langage.

La raison pour laquelle la création d’un tableau générique est interdite est que l’inheritance de type tableau, issu d’une époque pré-générique, est incompatible avec le système de type générique. Ie vous pouvez écrire:

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

A cet endroit, il faut souligner que, contrairement à certaines réponses, le compilateur ne fait pas d’ inférence de type sur l’expression List[]::new pour déduire le type d’élément générique List . Il est facile de prouver que la création de tableaux génériques est toujours interdite:

 IntFunction[]> af = List[]::new; // does not comstack 

Puisque List[]::new est illégal, il serait étrange que List[]::new soit accepté sans avertissement, en le déduisant comme étant la List[]::new illégale.

JLS §15.13 indique clairement:

Si une expression de référence de méthode se présente sous la forme ArrayType :: new , alors ArrayType doit indiquer un type qui est réifiable (§4.7) ou une erreur de compilation se produit.

Cela implique déjà que List[]::new est illégal, car List n’est pas reifiable, alors que List[]::new est légal, car List Est reifiable, et List[]::new est légal si nous considérons que List est un type brut , car la List types bruts est reifiable.

Ensuite, le §15.13.1 stipule:

Si l’expression de référence de la méthode a la forme ArrayType :: new , une méthode notionnelle unique est considérée. La méthode a un seul paramètre de type int , retourne ArrayType et n’a pas de clause throws . Si n = 1 , c’est la seule méthode potentiellement applicable; sinon, il n’y a pas de méthode potentiellement applicable.

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

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

sauf que la méthode create n’est que théorique. Et en effet, avec cette déclaration de méthode explicite, il n’y a que des avertissements de type brut à la méthode create , mais pas d’avertissement non vérifié concernant la conversion de List[] en List[] à la référence de méthode. Donc, il est compréhensible, ce qui se passe dans le compilateur dans le cas de List[]::new , où la méthode utilisant des types bruts est seulement théorique, c’est-à-dire qu’elle n’existe pas dans le code source.

Mais l’absence d’avertissements non vérifiés constitue une violation manifeste de la norme JLS §5.1.9, Conversion non contrôlée :

Soit G une déclaration de type générique avec n parameters de type.

Il y a une conversion non contrôlée de la classe brute ou du type d’interface (§4.8) G en n’importe quel type paramétré de la forme G .

Il y a une conversion non contrôlée du type tableau brut G[]ᵏ any à n’importe quel type de tableau de la forme G[]ᵏ . (La notation []ᵏ indique un type de tableau de k dimensions.)

L’utilisation d’une conversion non contrôlée entraîne un avertissement non vérifié à la compilation, sauf si tous les arguments de type T ᵢ (1 ≤ in ) sont des caractères génériques non liés (§4.5.1) ou si l’avertissement SuppressWarnings supprime l’avertissement non contrôlé (§9.6.4.5 ).

Ainsi, une conversion de List[] en List[] Est légale, car List est paramétré avec un caractère générique sans limite, mais la conversion de List[] en List[] doit produire un avertissement non contrôlé , ce qui est crucial ici, l’utilisation de List[]::new ne produit pas l’avertissement de type brut qui apparaît avec une méthode de création explicite. L’absence d’avertissement de type brut ne semble pas être une violation (pour autant que j’ai compris le §4.8 ) et cela ne poserait pas de problème si javac créait l’avertissement non contrôlé requirejs.

Le mieux que je puisse trouver est que le JLS spécifie qu’une référence de méthode au constructeur d’un type générique déduit les parameters génériques: “Si une méthode ou un constructeur est générique, les arguments de type appropriés peuvent être inférés ou fournis explicitement. ” Plus tard, il donne ArrayList::new comme exemple et le décrit comme “des arguments de type déduits pour la classe générique”, établissant ainsi que ArrayList::new (et non ArrayList<>::new ) est la syntaxe qui infère des arguments.

Étant donné une classe:

 public static class Test { public Test() {} } 

cela donne un avertissement:

 Test = new Test(); // No  

mais ce n’est pas le cas:

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

car Test::new infère implicitement les arguments génériques.

Je suppose donc qu’une référence de méthode à un constructeur de tableaux fonctionne de la même manière.

Il utilise toujours le type brut pour créer le tableau, non?

Les génériques Java ne sont qu’une illusion de compilation, de sorte que le type brut sera bien entendu utilisé lors de l’exécution pour créer le tableau.

Pourquoi le compilateur n’affiche-t-il pas l’avertissement dans le dernier cas?

Oui, la dissortingbution non vérifiée de Test[] à Test[] se produit toujours; ça se passe dans les coulisses dans un contexte anonyme.

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

Comme la méthode anonyme effectue le sale travail, la dissortingbution non contrôlée disparaît effectivement du code managé.