La première édition de ce livre date de 2001 (JDK 5) avec une maj en 2008 (JDK 6-9) et une 3e en 2017 (semble-t-il mineure puisque la cocarde JDK 9 est toujours présente sur la couverture). Le mot-clé var a été introduit durant le JDK 10 et n'était donc probablement pas vraiment dans l'esprit de l'auteur au moment de la rédaction.
Le mot-clé var permet l'inférence de type des variables locales ; et c'est bien là le cœur du sujet et le mot important : L O C A L.
Ce mot-clé permet donc de simplifier le code, son écriture, sa lecture, de réduire sa lourdeur ; une chose qui était déjà d'actualité et activement en débat parmi les créateurs du langage Java durant les confs de la JavaOne en 2011 (et probablement même auparavant). Cette petite chose qu'ils appellent le cérémoniel du code (ceremonial code) et qui peut rendre le code assez indigeste tant à l'écriture qu'à la lecture ou à sa maintenance (on ne manquera pas de faire un parallèle avec les langages humains tant écrits qu'oraux au fil des siècles).
De plus, ce mot-clé permet également de forcer un typage statique fort dans les blocs locaux ce qui rend impossible le changement de type concret et c'est fait exprès et ce pour éviter des bugs ou autres erreur de codage. Ainsi, si les classes Car et Bike héritent de Vehicle, ceci ne compile pas :
1 2
| var v = new Car();
v = new Bike(); /// Ne compile pas car v est de type Car. |
Il faudrait donc écrire à la place :
1 2
| Vehicle v = new Car();
v = new Bike(); |
Oui, il est tout à fait normal de prendre le le type parent des types des paramètres et du retour des fonctions (pour créer des interfaces passe-partout) ou celui des membres qu'on doit injecter, mais, la plupart du temps (ce qui ne veut pas dire tout le temps), cela ne sert a rien d'impacter les variables locales puisqu'elle sont privées par nature !
Quand le code local est de nature polymorphique (exemple ci-dessus) alors oui, il faut utiliser le type parent ; quand il ne l'est pas, non ce n'est pas nécessaire et c'est une source d'erreurs potentielles.
Je pourrai ajouter que, si ces variables sont affectées une seule fois, ça ne coûte pas grand chose de rajouter final devant mais les variables étant privées et d'une durée de vie limitée, ça re-complexifie le cérémoniel donc généralement les codeurs zapperont la chose...
Conclusion, remplacer :
ArrayList<String> vListe = new ArrayList<>(List.of(args));
par :
List<String> vListe = Arrays.asList(args);
me semble avoir peu d'interrêt.
Voir : Java 10 Local Variable Type Inference
Par contre, je suis un peu plus interloqué par la création de valeurDouble :
1 2 3 4 5 6 7 8 9 10 11 12
| private static double calculerMoyenne2PlusGrands(ArrayList<String> valeurs) {
// Convertir les chaînes de caractères en une liste de doubles
ArrayList<Double> valeursDouble = new ArrayList<>(valeurs.stream()
.map(Double::parseDouble)
.collect(Collectors.toList()));
// Trier la liste dans l'ordre décroissant
valeursDouble.sort((a, b) -> Double.compare(b, a));
// Calculer la moyenne des deux plus grandes valeurs
return (valeursDouble.get(0) + valeursDouble.get(1)) / 2.0;
} |
- Collectors.toList() retourne une List<Double> donc on a pas vraiment besoin de recréer une nouvelle ArrayList<Double> avec.
- Mais en fait c'est totalement inutile car :
- La méthode mapToDouble() du flux permet de convertir le Stream<String> en DoubleStream
- Le tri peut-être fait au niveau du flux via la méthode sorted(), pas besoin d'avoir une liste sous la main.
- Et on peut utiliser findFirst() pour trouver le bon résultat vu que le flux a été trié.
- Ou alors on peut utiliser la méthode max() mais cette dernière consomme et parcours tout le flux bien sur.
Donc :
- List<String> -> Stream<String> -> DoubleStream -> sorted() -> findFirst()
- ouList<String> -> Stream<String> -> DoubleStream -> max()
Bien évidement, il faudra faire attention aux performances si le flux est de taille importante (puisque certaines opération sur les flux sont terminales et d'autres non -faites au vol-).
Partager