Java 10 et le mot-clé var : problèmes potentiels 




Présentation

Java 10 introduit un nouveau mot-clé (var), qui permet d'éviter de typer explicitement une variable, mais reposant plutot sur le typage inféré (déduit par le compilateur).  Ce mot-clé est aussi utilisé en Javascript et C#, mais avec une fonctionalité qui n'est pas exactement la même.

L'introduction de ce mot-clé peut toutefois avoir pour effet de rendre le code Java moins robuste, en faisant apparaitre des erreurs auparavant détectées par le compilateur.

Cas simples : pas de problèmes.

L'utilisation de var avec des types de base n'est pas vraiment problématique.

void method {
    var filename = "notes.txt";
    var i = 10;
    var amount = 1000L;
    ...

Règle: Référer aux objets par leur interface 

Avant d'aller plus loin, revenons sur une bonne pratique énoncée par
Joshua Bloch.

Lorsqu'on déclare une variable, il est préférable de typer cette variable par une interface (si cela est possible) que par sa classe concrète. La bonne pratique se lit comme suit : Item 52 - Refer to objects by their interfaces From Effective Java 2/e by Joshua Bloch).
Exemple:

//Mauvais
ArrayList<String> names = new ArrayList<String>();
processNames(names);
//Bon
List<String> names = new ArrayList<>();
processNames(names);

Un avantage dans ce cas est de permettre à la méthode processName de prendre en paramètre n'importe quel List<String>; cette méthode devient donc plus réutilisable puisqu'on peut passer en paramètre n'importe quelle classe concrète implantant <String>.

La règle de Joshua Bloch peut être généralisée en référençant avec une classe générique, plutot que par une interface :

FileReader fr = new FileReader(filename);
readContent(fr);
Reader r = new FileReader(filename);
readContent(r);

Si readContent() accepte n'importe quel Reader, on pourra passer en paramètre n"importe quelle sous-classe, disons PrintWriter. 

Probleme #1 : non respect de la règle 52
 

var names = new ArrayList<String>();
  //is equivalent to
ArrayList<String> names = new ArrayList<String>();


Probleme #2 : provoquer un changement de comportement dans le code client
 

class MyCode {
  CityReader cityReader = ..
  var cities = cityReader.readCities(); //return List<String>
  CityUtil.processCities(cities);
  ..

en utilisant cette classe utilitaire :


//version 1
class CityUtil {
  public static void processCities(List<String> cities) {
    .. 

la classe utilitaire évolue:

//version 2
class CityUtil {
  public static void processCities(List<String> cities) {
    ..

  //added in version 2
  public static void processCities(CityList cities) { 
    ..

de même que CityReader

//version 2
class CityReader {
  public CityList readCities() { //changed in version 2
    ..
    CityList cityList = ..
    return cityList;
}

Mon code n'a pas change d'un iota, mais se comporte maintenant différemment:

class MyCode {
  CityReader cityReader = ..
  var cities = cityReader.readCities(); //return List<String> (no longer true)
  CityUtil.processCities(cities); //another version of processCities() is called
  ..

Si mon code n'avait pas utilise de var:

class MyCode {
  CityReader cityReader = ..
  List<String> cities = cityReader.readCities(); //compiler detects error
  CityUtil.processCities(cities);
  ..

Alors le changement de type dans
CityReader aurait provoquer une erreur de compilation.

Un cas légitime d'utilisation de var

void method {
    List<String> names = ..
    for (var name : names) {
      ..


Conclusion


Dernière mise à jour: 26 aout 2018.