Inférence généralisée de type cible en Java

1. Introduction

L'inférence de type a été introduite dans Java 5 pour compléter l'introduction des génériques et a été considérablement étendue dans les versions Java suivantes, également appelées Inférence de type cible généralisée.

Dans ce didacticiel, nous explorerons ce concept avec des exemples de code.

2. Génériques

Les génériques nous ont fourni de nombreux avantages tels qu'une sécurité de type accrue, éviter les erreurs de conversion de type et les algorithmes génériques. Vous pouvez en savoir plus sur les génériques dans cet article.

Cependant, l'introduction des génériques a entraîné la nécessité d'écrire du code standard en raison de la nécessité de passer des paramètres de type . Quelques exemples sont:

Map
    
      mapOfMaps = new HashMap
     
      (); List strList = Collections.emptyList(); List intList = Collections.emptyList();
     
    

3. Tapez Inference Before Java 8

Pour réduire la verbosité inutile du code, l'inférence de type a été introduite dans Java, qui consiste à déduire automatiquement des types de données non spécifiés d'une expression en fonction des informations contextuelles.

Maintenant, nous pouvons invoquer les mêmes types et méthodes génériques sans spécifier les types de paramètres. Le compilateur déduit automatiquement les types de paramètres si nécessaire.

On peut voir le même code en utilisant le nouveau concept:

List strListInferred = Collections.emptyList(); List intListInferred = Collections.emptyList(); 

Dans l'exemple ci-dessus, en fonction des types de retour attendus List et List , le compilateur peut déduire le paramètre type de la méthode générique suivante:

public static final  List emptyList() 

Comme nous pouvons le voir, le code résultant est concis. Maintenant, nous pouvons appeler des méthodes génériques comme une méthode ordinaire si le paramètre de type peut être déduit.

En Java 5, nous pourrions faire de l'inférence de type dans des contextes spécifiques comme indiqué ci-dessus.

Java 7 a élargi les contextes dans lesquels il pouvait être exécuté. Il a présenté l'opérateur diamant. Vous pouvez en savoir plus sur l'opérateur diamant dans cet article.

Maintenant, nous pouvons effectuer cette opération pour les constructeurs de classes génériques dans un contexte d'affectation. Un tel exemple est:

Map
    
      mapOfMapsInferred = new HashMap();
    

Ici, le compilateur Java utilise le type d'affectation attendu pour déduire les paramètres de type du constructeur HashMap .

4. Inférence généralisée de type cible - Java 8

Java 8 a encore élargi la portée de l'inférence de type. Nous appelons cette capacité d'inférence étendue l'inférence généralisée de type cible. Vous pouvez lire les détails techniques ici.

Java 8 a également introduit les expressions Lambda. Les expressions Lambda n'ont pas de type explicite. Leur type est déduit en regardant le type de cible du contexte ou de la situation. Le type cible d'une expression est le type de données attendu par le compilateur Java en fonction de l'endroit où l'expression apparaît.

Java 8 prend en charge l'inférence à l'aide de Target-Type dans un contexte de méthode. Lorsque nous invoquons une méthode générique sans arguments de type explicites, le compilateur peut examiner l'appel de méthode et les déclarations de méthode correspondantes pour déterminer le ou les arguments de type qui rendent l'appel applicable.

Regardons un exemple de code:

static  List add(List list, T a, T b) { list.add(a); list.add(b); return list; } List strListGeneralized = add(new ArrayList(), "abc", "def"); List intListGeneralized = add(new ArrayList(), 1, 2); List numListGeneralized = add(new ArrayList(), 1, 2.0);

Dans le code, ArrayList ne fournit pas explicitement l'argument de type. Ainsi, le compilateur doit l'inférer. Tout d'abord, le compilateur examine les arguments de la méthode add. Ensuite, il examine les paramètres passés à différentes invocations.

Il effectue une analyse d' inférence d'applicabilité d'appel pour déterminer si la méthode s'applique à ces appels . Si plusieurs méthodes sont applicables en raison d'une surcharge, le compilateur choisira la méthode la plus spécifique.

Ensuite, le compilateur effectue une analyse d' inférence de type d'appel pour déterminer les arguments de type. Les types de cibles attendus sont également utilisés dans cette analyse . Il déduit les arguments des trois instances comme ArrayList , ArrayList et ArrayList .

L'inférence de type cible nous permet de ne pas spécifier de types pour les paramètres d'expression lambda:

List intList = Arrays.asList(5, 2, 4, 2, 1); Collections.sort(intList, (a, b) -> a.compareTo(b)); List strList = Arrays.asList("Red", "Blue", "Green"); Collections.sort(strList, (a, b) -> a.compareTo(b));

Ici, les paramètres a et b n'ont pas de types explicitement définis. Leurs types sont déduits comme Integer dans la première expression Lambda et comme String dans la seconde.

5. Conclusion

Dans cet article rapide, nous avons examiné l'inférence de type, qui, avec les génériques et l'expression Lambda, nous permet d'écrire du code Java concis.

Comme d'habitude, le code source complet est disponible sur Github.