26 recommandations pour l'utilisation du type var en Java


Le Java Local Variable Type Inference (LVTI) ou, brièvement, le type var (l'identifiant var n'est pas un mot-clé, mais un nom de type réservé) a été ajouté à Java 10 à l'aide de JEP 286: Local-Variable Type Inference . Étant une fonction de compilation à 100%, elle n'affecte pas le bytecode, le runtime ou les performances. Fondamentalement, le compilateur vérifie le côté droit de l'opérateur d'affectation et, en fonction de celui-ci, détermine le type spécifique de la variable, puis le remplace par var .


De plus, il est utile pour réduire la verbosité du code passe-partout et accélère également le processus de programmation lui-même. Par exemple, il est très pratique d'écrire var evenAndOdd =... au lieu de Map<Boolean, List<Integer>> evenAndOdd =...


L'apparition de var ne signifie pas qu'il est toujours et pratique de l'utiliser partout, parfois il sera plus pratique de le faire avec des outils standards.


Dans cet article, nous examinerons 26 situations, avec des exemples de cas oĂą vous pouvez utiliser var et quand cela n'en vaut pas la peine.


Point 1: essayez de donner des noms significatifs aux variables locales


Habituellement, nous nous concentrons sur le fait de donner les noms corrects aux champs des classes, mais nous ne prêtons pas la même attention aux noms des variables locales. Lorsque nos méthodes sont parfaitement implémentées, contiennent peu de code et ont de bons noms, alors très souvent nous ne prêtons pas attention aux variables locales, ni même réduisons complètement leurs noms.


Lorsque nous utilisons var au lieu d'écrire des types explicites, le compilateur les détecte automatiquement et substitue var . Mais d'un autre côté, à la suite de cela, il devient plus difficile pour les gens de lire et de comprendre le code, car l'utilisation de var peut compliquer sa lisibilité et sa compréhension. Dans la plupart des cas, cela est dû au fait que nous avons tendance à considérer le type d'une variable comme information principale et son nom comme secondaire. Bien que ce soit exactement le contraire.


Exemple 1:


Beaucoup conviendront probablement que dans l'exemple ci-dessous, les noms des variables locales sont trop courts:


 // HAVING public boolean callDocumentationTask() { DocumentationTool dtl = ToolProvider.getSystemDocumentationTool(); DocumentationTask dtt = dtl.getTask(...); return dtt.call(); } 

Lorsque vous utilisez des noms courts, avec var , le code devient encore moins clair:


 // AVOID public boolean callDocumentationTask() { var dtl = ToolProvider.getSystemDocumentationTool(); var dtt = dtl.getTask(...); return dtt.call(); } 

Option plus préférée:


 // PREFER public boolean callDocumentationTask() { var documentationTool = ToolProvider.getSystemDocumentationTool(); var documentationTask = documentationTool.getTask(...); return documentationTask.call(); } 

Exemple 2:


Évitez de nommer des variables comme ceci:


 // AVOID public List<Product> fetchProducts(long userId) { var u = userRepository.findById(userId); var p = u.getCart(); return p; } 

Utilisez des noms plus significatifs:


 // PREFER public List<Product> fetchProducts(long userId) { var user = userRepository.findById(userId); var productList = user.getCart(); return productList; } 

Exemple 3:


Afin de donner des noms plus compréhensibles aux variables locales, n'allez pas aux extrêmes:


 // AVOID var byteArrayOutputStream = new ByteArrayOutputStream(); 

Au lieu de cela, vous pouvez utiliser une option plus courte mais non moins compréhensible:


 // PREFER var outputStream = new ByteArrayOutputStream(); // or var outputStreamOfFoo = new ByteArrayOutputStream(); 

Saviez-vous que Java a une classe interne nommée:
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState


Eh bien, nommer des variables avec ce type peut être délicat :)


Point 2: utilisez des littéraux pour aider à déterminer le type de primitive (int, long, float, double)


Sans l'utilisation de littéraux pour les types primitifs, nous pouvons constater que les types attendus et implicites peuvent différer. Cela est dû à la conversion de type implicite utilisée par les variables var .


Par exemple, les deux fragments de code suivants se comportent comme prévu. Ici, nous déclarons explicitement les types booléens et char :


 boolean flag = true; // this is of type boolean char a = 'a'; // this is of type char 

Maintenant, nous utilisons var , au lieu de déclarer explicitement des types:


 var flag = true; // this is inferred as boolean var a = 'a'; // this is inferred as char 

Jusqu'ici tout va bien. Faites maintenant la mĂŞme chose pour les types int , long , float et double :


 int intNumber = 20; // this is of type int long longNumber = 20; // this is of type long float floatNumber = 20; // this is of type float, 20.0 double doubleNumber = 20; // this is of type double, 20.0 

Bien que l'extrait de code ci-dessus soit simple et direct, utilisons maintenant var au lieu de spécifier explicitement les types.


A éviter:


 // AVOID var intNumber = 20; // this is inferred as int var longNumber = 20; // this is inferred as int var floatNumber = 20; // this is inferred as int var doubleNumber = 20; // this is inferred as int 

Les quatre variables seront sorties en tant qu'int . Pour corriger ce comportement, nous devons utiliser des littéraux Java:


 // PREFER var intNumber = 20; // this is inferred as int var longNumber = 20L; // this is inferred as long var floatNumber = 20F; // this is inferred as float, 20.0 var doubleNumber = 20D; // this is inferred as double, 20.0 

Mais que se passe-t-il si nous déclarons un nombre décimal?


Évitez cela si vous prévoyez d'obtenir une variable de type float :


 // AVOID, IF THIS IS A FLOAT var floatNumber = 20.5; // this is inferred as double 

Pour éviter toute surprise, utilisez le littéral approprié:


 // PREFER, IF THIS IS A FLOAT var floatNumber = 20.5F; // this is inferred as float 

Point 3: dans certains cas, les conversions de type var et implicite peuvent simplifier la prise en charge du code


Par exemple, supposons que notre code se situe entre deux méthodes. Une méthode permet d'obtenir un panier d'achat avec différents produits et de calculer le meilleur prix. Pour ce faire, il compare différents prix sur le marché et renvoie le prix total sous forme de flotteur . Une autre méthode déduit simplement ce prix de la carte.


Voyons d'abord une méthode qui calcule le meilleur prix:


 public float computeBestPrice(String[] items) { ... float price = ...; return price; } 

Deuxièmement, regardons la méthode qui fonctionne avec la carte:


 public boolean debitCard(float amount, ...) { ... } 

Maintenant, nous plaçons notre code entre ces deux méthodes de service externes en tant que client. Nos utilisateurs peuvent choisir les marchandises à acheter, nous calculons le meilleur prix pour eux, puis radions les fonds de la carte:


 // AVOID public void purchaseCart(long customerId) { ... float price = computeBestPrice(...); debitCard(price, ...); } 

Après un certain temps, la société propriétaire de l'API décide d'abandonner la représentation matérielle des prix au profit de la décimale (au lieu de float , int est désormais utilisé). Ils ont donc modifié le code API comme suit:


 public int computeBestPrice(String[] items) { ... float realprice = ...; ... int price = (int) realprice; return price; } public boolean debitCard(int amount, ...) { ... } 

Le fait est que notre code utilise une déclaration explicite d'une variable flottante comme prix. Dans sa forme actuelle, nous recevrons une erreur lors de la compilation. Mais si nous avions prévu une telle situation et utilisé var au lieu de float , alors notre code continuerait à fonctionner sans problème, grâce à la conversion de type implicite:


 // PREFER public void purchaseCart(long customerId) { ... var price = computeBestPrice(...); debitCard(price, ...); } 

Point 4: lorsque les littéraux ne sont pas une solution appropriée, utilisez une conversion explicite ou jetez var


Certains types primitifs en Java n'ont pas de littéraux spéciaux, par exemple des octets et des types courts . Dans ce cas, en utilisant la désignation de type explicite, nous pouvons créer des variables sans aucun problème.


Utilisez ceci au lieu de var :


 // PREFER THIS INSTEAD OF USING VAR byte byteNumber = 45; // this is of type byte short shortNumber = 4533; // this is of type short 

Mais pourquoi dans cette situation privilégier la notation de type explicite au lieu d'utiliser simplement var ? Eh bien, écrivons ce code en utilisant var . Notez que dans les deux cas, le compilateur supposera que vous avez besoin de variables de type int .


Évitez cette erreur:


 // AVOID var byteNumber = 45; // this is inferred as int var shortNumber = 4533; // this is inferred as int 

Il n'y a pas de littéraux ici qui pourraient nous être utiles, nous sommes donc obligés d'utiliser une conversion explicite de type descendant. Personnellement, j'éviterai de telles situations, car je ne vois aucun avantage ici.


N'utilisez cette entrée que si vous voulez vraiment utiliser var :


 // PREFER THIS ONLY IF YOU WANT TO USE VAR var byteNumber = (byte) 45; // this is inferred as byte var shortNumber = (short) 4533; // this is inferred as short 

Point 5: évitez d'utiliser var si les noms de variables ne contiennent pas suffisamment d'informations de type pour comprendre le code


L'avantage d'utiliser var est d'écrire du code plus concis. Par exemple, dans le cas de l'utilisation de constructeurs, nous pouvons éviter d'avoir à répéter le nom de classe et, par conséquent, éliminer la redondance de code.


Évitez ce qui suit:


 // AVOID MemoryCacheImageInputStream inputStream = new MemoryCacheImageInputStream(...); 

Utilisez plutĂ´t:


 // PREFER var inputStream = new MemoryCacheImageInputStream(...); 

Pour la construction ci-dessous, var sera également un bon moyen de simplifier le code sans perdre d'informations.


A éviter:


 // AVOID JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fm = compiler.getStandardFileManager(...); 

Utilisez le code suivant:


 // PREFER var compiler = ToolProvider.getSystemJavaCompiler(); var fileManager = compiler.getStandardFileManager(...); 

Alors, pourquoi sommes-nous plus à l'aise avec var dans les exemples présentés? Parce que toutes les informations nécessaires sont contenues dans les noms des variables. Mais si var , en combinaison avec un nom de variable, réduit la clarté du code, il vaut mieux refuser de l'utiliser.


A éviter:


 // AVOID public File fetchCartContent() { return new File(...); } // As a human, is hard to infer the "cart" type without // inspecting the fetchCartContent() method var cart = fetchCartContent(); 

Utilisation:


 // PREFER public File fetchCartContent() { return new File(...); } File cart = fetchCartContent(); 

Considérez, par exemple, l'utilisation de la classe java.nio.channels.Selector . Cette classe a une méthode open() statique qui renvoie un nouveau sélecteur et l'ouvre. Mais ici, vous pouvez facilement penser que la méthode Selector.open() peut renvoyer un type booléen , selon le succès de l'ouverture d'un sélecteur existant, ou même retourner void . L'utilisation de var ici entraînera une perte d'informations et une confusion dans le code.


Point 6: le type var garantit la sécurité à la compilation


Cela signifie que nous ne pouvons pas compiler une application qui tente d'effectuer des affectations incorrectes. Par exemple, le code ci-dessous ne compile pas:


 // IT DOESN'T COMPILE var items = 10; items = "10 items"; // incompatible types: String cannot be converted to int 

Mais celui-ci compile:


 var items = 10; items = 20; 

Et ce code compile avec succès:


 var items = "10"; items = "10 items"; 

Une fois que le compilateur a défini la valeur de la variable var , nous ne pouvons assigner rien d'autre que ce type.


Point 7: var ne peut pas être utilisé pour instancier un type spécifique et l'affecter à une variable de type d'interface


En Java, nous utilisons l'approche "programmation avec interfaces". Par exemple, nous créons une instance de la classe ArrayList, en l'associant à une abstraction (interface):


 List<String> products = new ArrayList<>(); 

Et nous évitons des choses comme lier un objet à une variable du même type:


 ArrayList<String> products = new ArrayList<>(); 

C'est la pratique la plus courante et la plus souhaitable, car nous pouvons facilement remplacer l'implémentation d'interface par une autre. Pour cela, il suffit de déclarer une variable de type interface.


Nous ne pourrons pas suivre ce concept en utilisant des variables var, car un type spécifique leur est toujours affiché. Par exemple, dans l'extrait de code suivant, le compilateur déterminera le type de la variable en tant que ArrayList<String> :


 var productList = new ArrayList<String>(); // inferred as ArrayList<String> 

Plusieurs arguments de défense expliquent ce comportement:


  • var est utilisĂ© pour les variables locales, oĂą, dans la plupart des cas, la programmation utilisant des interfaces est moins utilisĂ©e que dans les cas avec des paramètres de mĂ©thode renvoyĂ©s par des valeurs ou des champs


  • La portĂ©e des variables locales doit ĂŞtre petite, donc la rĂ©solution des problèmes causĂ©s par le passage Ă  une autre implĂ©mentation ne devrait pas ĂŞtre très difficile


  • var traite le code de droite comme l'initialiseur utilisĂ© pour dĂ©terminer le type rĂ©el. Si, Ă  un moment donnĂ©, l'initialiseur est modifiĂ©, le type en cours de dĂ©finition peut Ă©galement changer, provoquant des problèmes dans le code qui repose sur cette variable.



Paragraphe 8: la probabilité d'un type inattendu


L'utilisation de var en combinaison avec un opérateur diamant (<>) en l'absence d'informations pour identifier le type peut conduire à des résultats inattendus.


Avant Java 7, l'inférence de type explicite était utilisée pour les collections:


 // explicitly specifying generic class's instantiation parameter type List<String> products = new ArrayList<String>(); 

À partir de Java 7, l' opérateur diamant a été introduit. Dans ce cas, le compilateur dérivera indépendamment le type nécessaire:


 // inferring generic class's instantiation parameter type List<String> products = new ArrayList<>(); 

Quel type sera affiché dans le code ci-dessous?


Vous devez éviter de telles constructions:


 // AVOID var productList = new ArrayList<>(); // is inferred as ArrayList<Object> 

Le type sera défini comme ArrayList<Object> . En effet, les informations nécessaires pour déterminer correctement le type ne sont pas fournies. Cela conduit au fait que le type le plus proche sera sélectionné, ce qui peut être compatible avec le contexte de ce qui se passe. Dans ce cas, Object .


Ainsi, var ne peut être utilisé que si nous fournissons les informations nécessaires pour déterminer le type attendu. Le type peut être spécifié directement ou passé en argument.


Spécifiez directement le type:


 // PREFER var productList = new ArrayList<String>(); // inferred as ArrayList<String> 

Passez des arguments du type requis:


 var productStack = new ArrayDeque<String>(); var productList = new ArrayList<>(productStack); // inferred as ArrayList<String> 

 Product p1 = new Product(); Product p2 = new Product(); var listOfProduct = List.of(p1, p2); // inferred as List<Product> // DON'T DO THIS var listofProduct = List.of(); // inferred as List<Object> listofProduct.add(p1); listofProduct.add(p2); 

Élément 9: attribuer un tableau à une variable var ne nécessite pas de crochets []


Nous savons tous comment déclarer des tableaux en Java:


 int[] numbers = new int[5]; // or, less preferred int numbers[] = new int[5]; 

Que diriez-vous d'utiliser var lorsque vous travaillez avec des tableaux? Dans ce cas, il n'est pas nécessaire d'utiliser des supports sur le côté gauche.


Évitez ce qui suit (cela ne compile même pas):


 // IT DOESN'T COMPILE var[] numbers = new int[5]; // or var numbers[] = new int[5]; 

Utilisation:


 // PREFER var numbers = new int[5]; // inferred as array of int numbers[0] = 2; // work numbers[0] = 2.2; // doesn't work numbers[0] = "2"; // doesn't work 

Le code ci-dessous utilisant var ne parvient pas non plus à compiler. En effet, le compilateur ne peut pas déterminer le type du côté droit:


 // explicit type work as expected int[] numbers = {1, 2, 3}; // IT DOESN'T COMPILE var numbers = {1, 2, 3}; var numbers[] = {1, 2, 3}; var[] numbers = {1, 2, 3}; 

Élément 10: var ne peut pas être utilisé lors de la déclaration de plusieurs variables sur la même ligne


Si vous souhaitez déclarer des variables du même type à la fois, vous devez savoir que var ne convient pas pour cela. Le code suivant ne compile pas:


 // IT DOESN'T COMPILE // error: 'var' is not allowed in a compound declaration var hello = "hello", bye = "bye", welcome = "welcome"; 

Utilisez plutĂ´t:


 // PREFER String hello = "hello", bye = "bye", welcome = "welcome"; 

Ou est-ce:


 // PREFER var hello = "hello"; var bye = "bye"; var welcome = "welcome"; 

Point 11: les variables locales doivent s'efforcer de minimiser leur portée. Le type var renforce cette déclaration.


Gardez une petite portée pour les variables locales - je suis sûr que vous avez entendu cette déclaration avant var .


La lisibilité et la résolution rapide des bogues sont des arguments en faveur de cette approche. Par exemple, définissons une pile comme suit:


Évitez ceci:


 // AVOID ... var stack = new Stack<String>(); stack.push("George"); stack.push("Tyllen"); stack.push("Martin"); stack.push("Kelly"); ... // 50 lines of code that doesn't use stack // George, Tyllen, Martin, Kelly stack.forEach(...); ... 

Notez que nous appelons la méthode forEach() , héritée de java.util.Vector . Cette méthode passera par la pile comme n'importe quel autre vecteur et c'est ce dont nous avons besoin. Mais maintenant, nous avons décidé d'utiliser ArrayDeque au lieu de Stack . Lorsque nous faisons cela, la méthode forEach() recevra une implémentation de ArrayDeque qui traversera la pile comme une pile standard (LIFO)


 // AVOID ... var stack = new ArrayDeque<String>(); stack.push("George"); stack.push("Tyllen"); stack.push("Martin"); stack.push("Kelly"); ... // 50 lines of code that doesn't use stack // Kelly, Martin, Tyllen, George stack.forEach(...); ... 

Ce n'est pas ce que nous voulons. Il est trop difficile de suivre l'erreur ici, car le code contenant la partie forEach() n'est pas situé à côté du code dans lequel les modifications ont été apportées. Pour augmenter la vitesse de recherche et de correction des erreurs, il est préférable d'écrire du code à l'aide de la variable de stack , le plus près possible de la déclaration de cette variable.


Il est préférable de procéder comme suit:


 // PREFER ... var stack = new Stack<String>(); stack.push("George"); stack.push("Tyllen"); stack.push("Martin"); stack.push("Kelly"); ... // George, Tyllen, Martin, Kelly stack.forEach(...); ... // 50 lines of code that doesn't use stack 

Désormais, lorsque le développeur passera de Stack à ArrayQueue , il pourra rapidement remarquer l'erreur et la corriger.


Article 12: le type var simplifie l'utilisation de différents types dans les opérateurs ternaires


Nous pouvons utiliser différents types d'opérandes sur le côté droit de l'opérateur ternaire.


Lors de la spécification explicite de types, le code suivant ne se compile pas:


 // IT DOESN'T COMPILE List code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10); // or Set code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10); 

Néanmoins, nous pouvons le faire:


 Collection code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10); Object code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10); 

Le code ci-dessous ne compile pas non plus:


 // IT DOESN'T COMPILE int code = intOrString ? 12112 : "12112"; String code = intOrString ? 12112 : "12112"; 

Mais vous pouvez utiliser des types plus généraux:


 Serializable code = intOrString ? 12112 : "12112"; Object code = intOrString ? 12112 : "12112"; 

Dans tous ces cas, il vaut mieux préférer var :


 // PREFER // inferred as Collection<Integer> var code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10); // inferred as Serializable var code = intOrString ? 12112 : "12112"; 

Il ne résulte pas de ces exemples que le type var définit les types d'objet au moment de l'exécution. Ce n'est pas le cas!


Et, bien sûr, le type var fonctionnera correctement avec les mêmes types des deux opérandes:


 // inferred as float var code = oneOrTwoDigits ? 1211.2f : 1211.25f; 

Point 13: le type var peut être utilisé à l'intérieur des boucles


Nous pouvons facilement remplacer la déclaration explicite des types dans les boucles for par le type var .


Changer un type int explicite en var :


 // explicit type for (int i = 0; i < 5; i++) { ... } // using var for (var i = 0; i < 5; i++) { // i is inferred of type int ... } 

Changer le type explicite d' Order en var :


 List<Order> orderList = ...; // explicit type for (Order order : orderList) { ... } // using var for (var order : orderList) { // order type is inferred as Order ... } 

Point 14: var fonctionne bien avec les flux dans Java 8


Il est très simple d'utiliser var de Java 10 avec des flux apparus dans Java 8.


Vous pouvez simplement remplacer la déclaration explicite de type Stream par var :


Exemple 1:


 // explicit type Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5); numbers.filter(t -> t % 2 == 0).forEach(System.out::println); // using var var numbers = Stream.of(1, 2, 3, 4, 5); // inferred as Stream<Integer> numbers.filter(t -> t % 2 == 0).forEach(System.out::println); 

Exemple 2:


 // explicit types Stream<String> paths = Files.lines(Path.of("...")); List<File> files = paths.map(p -> new File(p)).collect(toList()); // using var var paths = Files.lines(Path.of("...")); // inferred as Stream<String> var files = paths.map(p -> new File(p)).collect(toList()); // inferred as List<File> 

Article 15: var peut être utilisé lors de la déclaration de variables locales destinées à casser de grandes chaînes d'expressions en parties


Les expressions avec beaucoup d'imbrication semblent impressionnantes et ressemblent généralement à une sorte de code intelligent et important. Dans le cas où il est nécessaire de faciliter la lisibilité du code, il est recommandé de décomposer une grande expression à l'aide de variables locales. Mais parfois, écrire beaucoup de variables locales semble être un travail très épuisant que j'aimerais éviter.


Un exemple d'une grande expression:


 List<Integer> intList = List.of(1, 1, 2, 3, 4, 4, 6, 2, 1, 5, 4, 5); // AVOID int result = intList.stream() .collect(Collectors.partitioningBy(i -> i % 2 == 0)) .values() .stream() .max(Comparator.comparing(List::size)) .orElse(Collections.emptyList()) .stream() .mapToInt(Integer::intValue) .sum(); 

Mieux décomposer le code en ses composants:


 List<Integer> intList = List.of(1, 1, 2, 3, 4, 4, 6, 2, 1, 5, 4, 5); // PREFER Map<Boolean, List<Integer>> evenAndOdd = intList.stream() .collect(Collectors.partitioningBy(i -> i % 2 == 0)); Optional<List<Integer>> evenOrOdd = evenAndOdd.values() .stream() .max(Comparator.comparing(List::size)); int sumEvenOrOdd = evenOrOdd.orElse(Collections.emptyList()) .stream() .mapToInt(Integer::intValue) .sum(); 

La deuxième version du code semble plus lisible et plus simple, mais la première version a également le droit d'exister. Il est tout à fait normal que notre esprit s'adapte à la compréhension de telles expressions et les préfère aux variables locales. Cependant, l'utilisation du type var peut aider à briser de grandes structures en réduisant l'effort de déclaration des variables locales:


 var intList = List.of(1, 1, 2, 3, 4, 4, 6, 2, 1, 5, 4, 5); // PREFER var evenAndOdd = intList.stream() .collect(Collectors.partitioningBy(i -> i % 2 == 0)); var evenOrOdd = evenAndOdd.values() .stream() .max(Comparator.comparing(List::size)); var sumEvenOrOdd = evenOrOdd.orElse(Collections.emptyList()) .stream() .mapToInt(Integer::intValue) .sum(); 

Article 16: var ne peut pas être utilisé comme type de retour ou comme type d'argument de méthode


Les deux extraits de code ci-dessous ne seront pas compilés.


Utilisation de var comme type de retour:


 // IT DOESN'T COMPILE public var countItems(Order order, long timestamp) { ... } 

Utilisation de var comme type d'argument de méthode:


 // IT DOESN'T COMPILE public int countItems(var order, var timestamp) { ... } 

Article 17: les variables locales de type var peuvent être passées comme paramètres de la méthode ou elles peuvent prendre la valeur retournée par la méthode


Les fragments de code suivants se compileront et fonctionneront correctement:


 public int countItems(Order order, long timestamp) { ... } public boolean checkOrder() { var order = ...; // an Order instance var timestamp = ...; // a long representing a timestamp var itemsNr = countItems(order, timestamp); // inferred as int type ... } 

:


 public <A, B> B contains(A container, B tocontain) { ... } var order = ...; // Order instance var product = ...; // Product instance var resultProduct = contains(order, product); // inferred as Product type 

18: var


:


 public interface Weighter { int getWeight(Product product); } // AVOID Weighter weighter = new Weighter() { @Override public int getWeight(Product product) { ... } }; Product product = ...; // a Product instance int weight = weighter.getWeight(product); 

var :


 public interface Weighter { int getWeight(Product product); } // PREFER var weighter = new Weighter() { @Override public int getWeight(Product product) { ... } }; var product = ...; // a Product instance var weight = weighter.getWeight(product); 

19: var effectively final


, :


… Java SE 8, , final effectively final. , , effectively final .

, var effectively final. .


:


 public interface Weighter { int getWeight(Product product); } // AVOID int ratio = 5; // this is effectively final Weighter weighter = new Weighter() { @Override public int getWeight(Product product) { return ratio * ...; } }; ratio = 3; // this reassignment will cause error 

:


 public interface Weighter { int getWeight(Product product); } // PREFER var ratio = 5; // this is effectively final var weighter = new Weighter() { @Override public int getWeight(Product product) { return ratio * ...; } }; ratio = 3; // this reassignment will cause error 

20: var- final-


var ( , effectively final). , final .


:


 // AVOID // IT DOESN'T COMPILE public void discount(int price) { final int limit = 2000; final int discount = 5; if (price > limit) { discount++; // this reassignment will cause error, which is ok } } 

:


 // PREFER // IT DOESN'T COMPILE public void discount(int price) { final var limit = 2000; final var discount = 5; if (price > limit) { discount++; // this reassignment will cause error, which is ok } } 

21:


var , . , var , :


 // IT DOESN'T COMPILE // lambda expression needs an explicit target-type var f = x -> x + 1; // method reference needs an explicit target-type var exception = IllegalArgumentException::new; 

:


 // PREFER Function<Integer, Integer> f = x -> x + 1; Supplier<IllegalArgumentException> exception = IllegalArgumentException::new; 

Java 11 var - . Java 11:


 // Java 11 (var x, var y) -> x + y // or (@Nonnull var x, @Nonnull var y) -> x + y 

22: var null'


var - .


( null ):


 // IT DOESN'T COMPILE var message = null; // result in an error of type: variable initializer is 'null' 

( ):


 // IT DOESN'T COMPILE var message; // result in: cannot use 'var' on variable without initializer ... message = "hello"; 

:


 // PREFER String message = null; // or String message; ... message = "hello"; 

23: var


var , .


:


 // IT DOESN'T COMPILE public class Product { private var price; // error: 'var' is not allowed here private var name; // error: 'var' is not allowed here ... } 

:


 // PREFER public class Product { private int price; private String name; ... } 

24: var catch


, try-with-resources


catch


, , .


:


 // IT DOESN'T COMPILE try { TimeUnit.NANOSECONDS.sleep(5000); } catch (var ex) { ... } 

:


 // PREFER try { TimeUnit.NANOSECONDS.sleep(5000); } catch (InterruptedException ex) { ... } 

Try-with-resources


, var try-with-resources .


, :


 // explicit type try (PrintWriter writer = new PrintWriter(new File("welcome.txt"))) { writer.println("Welcome message"); } 

var :


 // using var try (var writer = new PrintWriter(new File("welcome.txt"))) { writer.println("Welcome message"); } 

25: var


, :


 public <T extends Number> T add(T t) { T temp = t; ... return temp; } 

, var , T var :


 public <T extends Number> T add(T t) { var temp = t; ... return temp; } 

, var :


 codepublic <T extends Number> T add(T t) { List<T> numbers = new ArrayList<>(); numbers.add((T) Integer.valueOf(3)); numbers.add((T) Double.valueOf(3.9)); numbers.add(t); numbers.add("5"); // error: incompatible types: String cannot be converted to T ... } 

List<T> var :


 public <T extends Number> T add(T t) { var numbers = new ArrayList<T>(); // DON'T DO THIS, DON'T FORGET THE, T var numbers = new ArrayList<>(); numbers.add((T) Integer.valueOf(3)); numbers.add((T) Double.valueOf(3.9)); numbers.add(t); numbers.add("5"); // error: incompatible types: String cannot be converted to T ... } 

26: var Wildcards (?),


? Wildcards


var :


 // explicit type Class<?> clazz = Integer.class; // use var var clazz = Integer.class; 

Foo<?> var , , var .


, , , , . , ArrayList , Collection<?> :


 // explicit type Collection<?> stuff = new ArrayList<>(); stuff.add("hello"); // compile time error stuff.add("world"); // compile time error // use var, this will remove the error, but I don't think that this is // what you had in mind when you wrote the above code var stuff = new ArrayList<>(); strings.add("hello"); // no error strings.add("world"); // no error 

(Foo <? extends T>) (Foo <? super T>)


, :


 // explicit type Class<? extends Number> intNumber = Integer.class; Class<? super FilterReader> fileReader = Reader.class; 

, , :


 // IT DOESN'T COMPILE // error: Class<Reader> cannot be converted to Class<? extends Number> Class<? extends Number> intNumber = Reader.class; // error: Class<Integer> cannot be converted to Class<? super FilterReader> Class<? super FilterReader> fileReader = Integer.class; 

var :


 // using var var intNumber = Integer.class; var fileReader = Reader.class; 

, . – :


 // this will compile just fine var intNumber = Reader.class; var fileReader = Integer.class; 

Conclusion


« var », Java 10. , . , var , .


var Java!

Source: https://habr.com/ru/post/fr438206/


All Articles