[JAVA] [JDK 10] Inférence de type à l'aide de var

Cet article est le 16e jour du Java Advent Calendar 2017.

Considérons le contenu et l'utilisation de JEP 286: Inference de type de variable locale qui sera ajouté dans JDK 10.

(Ajouté le 22.03.2018) À propos de la vérification du fonctionnement avec la version officielle JDK10

La version officielle de JDK10 est sortie le 20 mars 2018, j'ai donc vérifié s'il y avait des changements depuis que j'ai écrit cet article.

C:\Users\chooyan>"C:\Program Files\java\jdk-10\bin\javac.exe" -version
javac 10

En conséquence, dans la mesure où j'ai vérifié l'opération et regardé la page suivante, il semblait qu'il n'y avait pas de changement dans le contenu décrit dans cet article.

Java 10 Local Variable Type Inference | Oracle Developers

En passant, l'article ci-dessus mentionne également le comportement lorsque vous écrivez var list = new ArrayList <> ();, dont il a été question dans la section commentaires de cet article.

En conclusion, il est traité comme ʻArrayList `et il n'y a pas de mérite particulier, alors évitez-le.

(Ajout jusqu'à présent)

Audience de cet article

Cet article

  • Je travaille avec Java
  • Le contenu de JEP 286 n'a pas encore été vérifié.
  • J'ai peu d'expérience avec des langages autres que Java

Cet article est destiné à ceux qui disent.

Puisque var est un mot-clé qui apparaît souvent dans des langues autres que Java, certaines personnes peuvent s'attendre à le comparer, mais je n'ai pas les connaissances et l'expérience nécessaires pour écrire à ce sujet, alors cet article Ensuite, le contenu est comme l'introduction du nouveau var se concentrant uniquement sur Java.

Je l'ai écrit pour que ce soit le meilleur article pour ceux qui veulent se préparer à peu près à Java var, donc j'espère que vous lirez à l'avance sur cette base.

JDK à utiliser

Cet article utilise JDK 10 Early-Access Builds.

En téléchargeant et en installant le JDK et en utilisant javac et jshell dans le dossier bin, vous pouvez essayer le code source en utilisant var comme l'exemple de source dans cet article.

C:\Users\chooyan>"C:\Program Files\java\jdk-10\bin\javac.exe" -version
javac 10-ea

Vue d'ensemble de var

Ce qui est envisagé dans JEP 286: Inférence de type de variable locale est d'utiliser var pour spécifier le type lors de la déclaration d'une variable. Soyons capables de simplifier le code pour.

Avant de plonger dans cela, jetons un coup d'œil à l'exemple de code. Dans cet article, nous utiliserons jshell pour un exemple de code qui peut être complété en quelques lignes.

Voici un exemple de déclaration d'une variable de type int.

jshell


jshell> int limit = 20; //Déclarez une variable de type int de manière conventionnelle
limit ==> 20

jshell> var limit = 20; //Déclarer une variable de type int en utilisant var
limit ==> 20

La déclaration de type de ʻinta été remplacée parvar`.

La même chose est vraie pour les types d'objets. Jetons un coup d'œil à certains.

jshell


jshell> String name = "chooyan_eng"; //Déclarez une variable de type String de manière conventionnelle
name ==> "chooyan_eng"

jshell> var name = "chooyan_eng"; //Déclarer une variable de type String à l'aide de var
name ==> "chooyan_eng"

jshell> List<String> idList = new ArrayList<>(); //Liste de manière traditionnelle<String>Déclarer une variable de type
idList ==> []

jshell> var idList = new ArrayList<String>(); //Liste à l'aide de var<String>Déclarer une variable de type
idList ==> []

De plus, si le côté droit est une méthode, vous pouvez utiliser var car le type de retour est clair de cette signature.

jshell


jshell> private int sum(int x, int y) {
   ...>     return x + y;
   ...> }

|Créé le suivant:Somme de la méthode(int,int)

jshell> var s = sum(4, 5);

s ==> 9

Qu'est-ce que tu penses. Comme mentionné ci-dessus, la nouvelle inférence de type Java considérée dans JEP286 est de réduire la quantité de frappe et de rendre le code plus facile à lire en remplaçant la spécification de type sur le côté gauche par «var».

limite de var

Eh bien, j'ai écrit que vous pouvez simplifier la spécification de type sur le côté gauche en utilisant le nouveau var, mais il n'est pas toujours possible d'utiliser var.

Pour utiliser «var», les conditions suivantes doivent être remplies.

  • Une variable locale déclarée dans la méthode
  • Le type est clair lorsque vous regardez le côté droit

Comme prémisse, «var» de Java est __ "Puisque le type du côté gauche peut être déterminé au moment de la compilation en regardant le côté droit, la spécification du type du côté gauche peut être omise au moment du codage" __. Ce n'est pas un mécanisme qui "détermine le type au moment de l'exécution".

Pour expliquer un peu plus en détail, par exemple, si vous créez une instance de la classe ʻArticle et l'assignez à la variable ʻarticle, jusqu'à présent

jshell


jshell> Article article = new Article();

article ==> Article@59494225

J'ai écrit. Si vous regardez attentivement, tant que l'instance créée sur le côté droit est de type ʻArticle, le type de la variable pour l'affecter doit également être de type ʻArticle (ou sa classe parente). Et c'est toujours au compilateur de faire une erreur si ce n'est pas le cas.

Ensuite, si vous n'écrivez que «var» sur le côté gauche et permettez au compilateur de déduire le type réel de la variable, c'est pratique car vous n'avez pas à écrire la même classe deux fois sur une ligne. Est-ce l'idée du var de Java.

Inversement, «var» ne peut pas être utilisé dans des situations où le type n'est pas clairement déterminé en regardant le côté droit au moment de la compilation. De là, je vais vous expliquer dans l'ordre quel type de cas "le type n'est pas clairement déterminé même si vous regardez du côté droit".

Lorsque le type ne peut pas être déterminé simplement en regardant le côté droit

Dans certains cas, le type ne peut pas être déterminé simplement en regardant le côté droit.

Le côté droit est nul

Je pense qu'il n'est pas rare d'attribuer «null» (ou rien) lors de la déclaration d'une variable.

Par exemple


private Article getArticle(int id) {
    Article article = null;

    if (isOnline()) {
        article = requestArticle(id);
    } else {
        article = selectArticleFromCache(id);
    }

    //Traitement de la variable article

    return article;
}

Si vous voulez changer la méthode d'affectation dans l'instruction if, mais souhaitez accéder à la variable en dehors de l'instruction if, déclarez la variable ʻarticle` avant l'instruction if et initialisez-la avec null. besoin de le faire.

Mais à ce moment

Article article = null;

Déclaration de variable

var article = null;

Ne peut pas être écrit. C'est parce que le côté droit de cette ligne est nul, donc le compilateur ne peut pas déterminer quel type est assigné à la variable ʻarticle`.

Quand je l'essaye avec jshell,

jshell


jshell> var article = null;

|Erreur:
|  cannot infer type for local variable article
|    (variable initializer is 'null')
|  var article = null;
|  ^--------------^

Donc, j'obtiens une erreur disant que l'inférence de type n'est pas possible avec null.

Ne peut pas être utilisé avec le type lambda

Ensuite, comme une situation où «var» ne peut pas être utilisé, il y a un cas où le type sur le côté droit est omis par l'expression lambda.

Par exemple, si vous souhaitez créer une classe anonyme qui hérite de la classe Runnable comme indiqué ci-dessous, vous pouvez écrire une expression lambda dans la version actuelle de Java comme suit.

jshell


jshell> Runnable runner = () -> { System.out.println("run!"); };

runner ==> $Lambda$15/1471868639@343f4d3d

D'un autre côté, quand j'essaye d'écrire ceci en utilisant le nouveau var, cela ressemble à ceci:

jshell


jshell> var runner = () -> { System.out.println("run!"); };

|Erreur:
|  cannot infer type for local variable runner
|    (lambda expression needs an explicit target-type)
|  var runner = () -> { System.out.println("run!"); };
|  ^--------------------------------------------^

Comme il n'y a pas de spécification de type explicite dans l'expression lambda sur le côté droit, il y a un message indiquant que «var» sur le côté gauche ne peut pas déterminer le type. En d'autres termes, l'omission de type par expression lambda et «var» ne peuvent pas être utilisés en même temps.

En passant, dans un tel cas, vous pouvez éviter l'erreur en convertissant explicitement le type comme suit.

jshell


jshell> var runner = (Runnable) (() -> {System.out.println("run!");})
runner ==> $Lambda$15/87765719@5442a311

Cependant, dans ce cas, il est plus facile de lire le code en spécifiant le type sur le côté gauche sans utiliser var, donc vous n'avez pas besoin de l'écrire comme ça.

jshell


jshell> Runnable runner = () -> {System.out.println("run!");};
runner ==> $Lambda$15/87765719@5442a311

Ne peut pas être utilisé avec des variables d'instance

Comme mentionné précédemment, «var» ne peut être utilisé que pour des «variables locales». En d'autres termes, si vous essayez de l'utiliser pour une variable d'instance comme indiqué ci-dessous, une erreur se produira.

jshell


jshell> class Article {
   ...>     var id = 0;
   ...>     var title = "";
   ...> }

|Erreur:
|  'var' is not allowed here
|      var id = 0;
|      ^-^
|Erreur:
|  'var' is not allowed here
|      var title = "";
|      ^-^

Le message d'erreur reste le même. On dit que "il ne peut pas être utilisé ici".

Je ne connais pas les détails, mais cela signifie-t-il que seules les variables locales sont restreintes parce qu'il devient difficile de juger à quel moment la portée devient large? Cette zone n'a pas été étudiée (désolé), mais cela semble être une telle chose.

Ne peut pas être utilisé pour initialiser un tableau sans déclaration de type

Vous ne pouvez pas utiliser var lors de la création d'un tableau sans la déclaration de type, comme indiqué ci-dessous.

jshell


jshell> var arr = {1, 2, 3}

|Erreur:
|  cannot infer type for local variable arr
|    (array initializer needs an explicit target-type)
|  var arr = {1, 2, 3};
|  ^------------------^

Par contre, si vous spécifiez le type avec new int [], il n'y a pas de problème.

jshell



jshell> var arr = new int[]{1, 2, 3}
arr ==> int[3] { 1, 2, 3 }

Il est moins probable d'utiliser «var» comme montant du type, mais ici, comme ce dernier, déclarer avec «var» après avoir spécifié le type peut être cohérent avec d'autres méthodes de déclaration. Je me suis senti comme.

FAQ avec l'introduction de var

De plus, j'aimerais réfléchir à ce qui semble discutable avec l'introduction de var dans un format de questions-réponses.

Q1. Et si je veux le rendre "final"?

Par exemple, dans le cas de Kotlin, les variables qui peuvent être réaffectées sont «var» et les variables qui ne peuvent pas être réaffectées sont «val».

Java n'a actuellement rien qui représente une telle "var non réassignable". Il suffit de mettre «final» devant le moule comme avant.

VarSample.java


public class VarSample {
    public static void main(String[] args) {
        final var id = 2;
        id = 3;
        System.out.println(id);
    }
}

Si vous compilez le code ci-dessus, vous pouvez voir qu'il n'est plus possible d'affecter des variables avec final.

$ javac VarSample.java

VarSample.java:4:Erreur:Vous ne pouvez pas attribuer une valeur à l'ID de variable final
        id = 3;
        ^

Ensuite, devrais-je écrire la constante comme var final statique public? Cependant, cela a la limitation que var ne peut être utilisé que pour les variables locales, donc la constante reste la même qu'avant.


public static final String DEFAULT_ID = "9999";

Il est décrit comme.

Le seul qualificatif qui peut être attaché à une variable locale est «final», donc vous ne penserez probablement pas aux compromis avec d'autres qualificatifs.

Q2. Je souhaite le recevoir à l'interface

var reçoit le côté droit avec le type déduit du côté droit.

En d'autres termes, il n'est pas possible de recevoir la classe concrète générée dans l'interface, qui était écrite jusqu'à présent comme un cliché.

jshell


//ArrayList généré<String>Une instance de List, qui est une interface<String>Recevoir à
jshell> List<String> idList = new ArrayList<>();  

jshell


//Vous ne pouvez pas spécifier le type à recevoir lors de l'utilisation de var
jshell> var idList = new ArrayList<String>();  

Si vous prenez l'habitude de "gérer les objets dans l'interface autant que possible!", Cela peut sembler un changement très difficile pendant un moment, mais en réalité, c'est rarement gênant.

Certes, s'il est reçu par une classe concrète, cela entraînera des inconvénients lors de l'affectation d'une autre classe avec la même interface que celle illustrée ci-dessous.

jshell


jshell> var idList = new ArrayList<String>();
idList ==> []

jshell> idList = new LinkedList<String>();

|Erreur:
|Type incompatible: java.util.LinkedList<java.lang.String>Vers java.util.ArrayList<java.lang.String>Ne peut pas être converti en:
|  idList = new LinkedList<String>();
|           ^----------------------^

Cependant, vous devriez rarement écrire du code comme celui-ci en premier lieu.

En premier lieu, il est géré dans l'interface afin que toute classe concrète puisse être traitée de la même manière qu'un argument de méthode ou une valeur de retour (la classe concrète peut être commutée selon les besoins).

Et une telle utilisation est toujours possible avec «var».

jshell


jshell> private List<String> getIdList() {
   ...>     if (isArrayListSuitable()) {
   ...>         return new ArrayList<String>();
   ...>     } else if (isLinkedListSuitable()) {
   ...>         return new LinkedList<String>();
   ...>     }
   ...>     return new ArrayList<String>();
   ...> }
|Créé le suivant:Méthode getIdList()

jshell> var idList = getIdList();
idList ==> []

Après tout, même si var est automatiquement reçu dans une classe concrète, il a un impact limité à moins qu'il ne soit disponible uniquement lors de la déclaration d'une variable locale.

Q3. J'ai déjà créé une classe nommée var

C'est un problème. Le problème est que j'ai créé un nom de classe appelé "var" qui s'écarte de la convention de dénomination Java.

En fait, même dans JEP286, l'un des risques de l'introduction de «var» est le problème de compatibilité lorsque la classe «var» est déjà définie.

Risk: source incompatibilities (someone may have used var as a type name.)

Cependant, en premier lieu, il est peu probable que vous utilisiez un mot commençant par une minuscule comme nom de classe, ou même le mot "var" qui est souvent utilisé comme mot-clé dans d'autres langues comme nom de type, donc la direction pour faire de "var" un nouveau mot réservé Il est écrit cela ne change pas.

Mitigated with reserved type names; names like var do not conform to the naming conventions for types, and therefore are unlikely to be used as types. The name var is commonly used as an identifier; we continue to allow this.

Si vous avez déjà créé une classe appelée var, vous devrez la renommer avant de migrer vers JDK 10.

Il n'y a pas de problème particulier avec le nom de variable "var". C'est un peu difficile à lire, mais il peut être utilisé en conjonction avec le mot-clé var.

jshell


jshell> int var = 1; //Définir la variable de type int var
var ==> 1

jshell> var = 2; //Attribuer une autre valeur à la variable var
var ==> 2

jshell> var var = 1; //Définir la variable var à l'aide de var (le type int est inféré)
var ==> 1

Résumé

Comme mentionné ci-dessus, j'ai vérifié le var qui sera introduit dans JDK 10 en utilisant la version d'accès anticipé actuellement publiée de JDK 10.

Pour ceux qui utilisent des langages tels que JavaScript, quand vous entendez var, vous pouvez avoir l'impression que Java devient un langage dont le type change dynamiquement, mais le var de Java est" côté gauche ". Je pense qu'il a été entendu que le comportement ne change pas en particulier car il s'agit de "la spécification de type de" peut être omis.

Lorsque «var» sera disponible, on s'attend à ce que différentes normes de codage et coutumes émergent, donc je pense qu'il peut être accepté sans problème en l'imaginant à partir de maintenant. pense. Veuillez supprimer la version d'accès anticipé de JDK10 et l'essayer.

De plus, je n'ai pas encore lu et compris le contenu décrit dans JEP 286, il peut donc y avoir des erreurs dans le contenu. ne pas. Si vous avez des points étranges, veuillez commenter.


Publicité

L'autre jour, j'ai écrit un article j'ai fait un référentiel "Code Your Ruby" pour les personnes qui veulent travailler en Ruby.

__ "Je suis ingénieur Java maintenant, mais je veux travailler sur Ruby à partir de maintenant" __ Un référentiel appelé Code Your Ruby qui est idéal pour étudier Nous l'avons présenté, donc si vous pensez "c'est vous-même!", Veuillez le lire.

Recommended Posts