Les nouveautés du langage dans Java 7

le 14/11/2008 par Marc Bojoly
Tags: Software Engineering

La nouvelle version de Java 7 est en cours de préparation. Malgré le retard et les incertitudes sur le contenu, nous considérons que Java 7 constitue une opportunité intéressante pour le langage. Après un premier article consacré aux nouvelles librairies proposées pour Java 7, ce second article introductif vous propose de découvrir les nouvelles fonctionnalités du langage et de la JVM. Là encore, en cas de doute sur le contenu de la version finale, nous préciserons les réserves concernant une librairie ou une syntaxe en indiquant si l'exemple de code proposé compilait avec la version de prévisualisation 1.7.0-ea-b37.

Support des propriétés JavaBean

En Java, il est nécessaire d'implémenter des accesseurs (getter et setter) pour lire et modifier les attributs d'un objet. En effet, rendre public un attribut est une (très) mauvaise pratique puisque lorsque le besoin d'ajouter un comportement lors de la lecture ou de la modification de cet attribut apparaitra, tous les accès à cet attribut devront être remplacés par des appels aux accesseurs (qui implémentent ce nouveau comportement). La notion de propriété est proche de celle d'un attribut à ceci près qu'un getter et un setter peuvent lui être associés sans que l'appelant ne soit impacté.

Ainsi une propriété peut-être déclarée public :

class Product {
  public property String name; 
} ... 
product->name = "Java";

Dans cet exemple, la propriété name se comporte comme un attribut public (notez cependant la nouvelle syntaxe d'accès à la propriété, ->, qui n'est pas sûr d'être celle retenue dans la version finale). Lorsque le besoin d'implémenter un getter et/ou un setter se fait sentir :

class Product {  
  private String _name;  
  public property String name   
  get {    // do something    ...;    return _name;   }   
  set(String newName) {     // do something     ...;     _name = newName;   } 
}

Après cette évolution, le code appelant n'a pas besoin d'être modifié, les nouveaux getter et setter seront appelés. Les propriétés permettent donc de s'affranchir d'écrire des getter et setter qui ne font rien de plus que lire et modifier un attribut (même si nos IDE le font pour nous aujourd'hui) mais aussi d'alléger le code des classes de type JavaBean.

Le compilateur de la prévisualisation ne supporte pas encore cette fonctionnalité.

Support des opérateurs numérique pour le type BigDecimal

Lorsqu'il s'agit d'effectuer des calculs avec la classe BigDecimal de Java, cela devient vite illisible :

bd1.multiply(bd2).divide(bd3.add(bd4))

En ajoutant le support des opérateurs numériques à cette classe, on obtiendrait alors un code plus clair :

bd1 * bd2 / (bd3 + bd4)

Le compilateur de la prévisualisation ne supporte pas encore cette fonctionnalité.

Comparaison pour les énumérations

Cette proposition permet de donner un ordre aux valeurs d'une énumération et ainsi de pouvoir comparer leur ordre.

enum LevelEnum { level1, level2, level3, level4, level5 }; 
if (currentLevel > LevelEnum.level3) ...

Le compilateur de la prévisualisation ne supporte pas encore cette fonctionnalité.

Invocation chainée

L'invocation chainée de méthodes d'un même objet consiste à renvoyer l'objet en question pour ses méthodes qui ont déclarées void comme type de retour. De cette façon, il est alors possible d'appeler une seconde méthode de cet objet après l'appel d'une méthode qui ne renvoie rien.

new JFrame()  .setBounds(0, 0, 100, 100)  .setName("MainFrame")  .setLocation(200, 200);

L'intérêt est d'alléger le code répétitif et de pouvoir pousser un peu plus la création d'API dites fluides (ou "fluent API"). Le compilateur de la prévisualisation ne supporte pas encore cette fonctionnalité.

Méthodes d'extension

Les méthodes d'extension sont des méthodes statiques qui peuvent être utilisées comme s'il s'agissait de méthodes de la classe de leur premier argument.

Class Collections {   
  public static T max(Collection collection) { ... } 
}

import static java.util.Collections.max; Collection list = new ArrayList(); list.max();

La méthode max est appliquée sur l'objet list comme s'il s'agissait d'une méthode de la classe Collection. Avec les méthodes d'extension, ceci est possible car list est du type du premier argument de la méthode statique max. La dernière ligne revient à écrire :

Collections.max(list);

Le compilateur de la prévisualisation ne supporte pas encore cette fonctionnalité.

Clause catch améliorée

La clause catch améliorée permet de traiter plusieurs exceptions dans le même bloc en utilisant l'opérateur '|' :

try {  ... } 
catch (IOException | RuntimeException exception) 
{  ... // le même traitement est appliqué pour les 2 types d'exceptions }

De plus, la possibilité de renvoyer une exception même si la classe déclarée dans la clause catch est parente de la classe réelle de l'exception sans avoir à l'encapsuler dans une nouvelle exception d'un type déclaré par la méthode (comme c'est nécessaire de faire actuellement).

public void throwAnException() throws IOException, RuntimeException {  
try {    ...  } 
catch (Exception exception) {    
  // Code actuel:    
  // throw new IOException(exception);    
  // ou    
  // throw new RuntimeException(exception);   
  // Code avec la clause catch améliorée    
  throw exception;  
 } 
}

Le compilateur de la prévisualisation ne supporte pas encore cette fonctionnalité.

Instruction invokedynamic et langages dynamiques

invokedynamic est une nouvelle instruction du bytecode de la JVM qui permet de simplifier l'implémentation de langages dynamiques (tels que JRuby, JPython ou encore Groovy). En effet, pour ces langages, les types des objets sur lesquels sont invoquées les méthodes ne sont pas connus à la compilation mais à l'exécution. De plus, il n'est pas possible de savoir si une méthode existe ou non pour une classe puisqu'elles peuvent être ajoutées ou supprimées à l'exécution. Les instructions actuelles (invokeinterface et invokevirtual) du bytecode Java nécessitent un type bien définit pour exécuter une méthode. Les implémentations actuelles de ces langages dynamiques sur la JVM doivent donc générer ces types à l'exécution. invokedynamic supprime cette limitation et améliore alors les performances de ces langages.

Support natif XML

Le support d'une classe XML avec une syntaxe native au langage Java est une des propositions d'inclusion pour la version 7. En effet, au même titre que la classe String qui possède une syntaxe particulière (les guillemets) pour construire une nouvelle chaine de caractère, une syntaxe particulière pour construire des documents XML permettrait de réduire grandement la verbosité du code Java nécessaire pour créer de telles structures avec les API existantes. Parmi les syntaxes proposées, on trouve :

public XML createProduct(String name, String version) {  
  XML product = <product>
                          <name>{name}</name>
                          <version>{version}</version>
                       </product>;  
         return product; 
}

Cette syntaxe a l'avantage d'être du XML mais utilise les éléments syntaxiques des génériques. Une autre syntaxe pourrait être : public XML createProduct(String name, String version) {

XML product = #product { 
                        #name { name },               
                        #version { version }       
       }; 
    return product; }

Les expressions entre accolades sont des expressions Java. À cette proposition s'ajoute la notion de DataCoder qui permettrait d'encoder et décoder un objet Java en chaine de caractères (pour être placé comme contenu d'une balise XML par exemple) et inversement. Pour ce qui est du streaming XML, les objets XMLSource pour lire (hasNext(), match(String xpath) et match()) et XMLSink pour écrire permettraient de simplifier la lecture et l'écriture au fil de l'eau de flux XML.

Le compilateur de la prévisualisation ne supporte pas encore cette fonctionnalité.

Comparaisons de chaines de caractères dans la clause switch

Une des propositions d'évolution pour Java 7 est la possibilité d'utiliser des chaines de caractères dans les clauses case d'un switch :

switch (str) {    
  case "true":        
    return true;    
  case "false":        
    return false; 
}

Le compilateur de la prévisualisation ne supporte pas encore cette fonctionnalité.

Tiered Compilation

En plus du mode client et du mode serveur actuels, un nouveau mode de compilation JIT appelé Tiered Compilation permettra à la JVM de profiter à la fois d'un démarrage rapide proposé par le mode client et d'optimisations plus agressives qu'offre le mode serveur actuel. En fait, il améliorera le mode client actuel en supprimant certaines compilations inutiles car réalisées trop top dans le démarrage de la JVM. Par exemple, certaines méthodes sont compilées et inlinées car au démarrage il n'y a qu'une possibilité d'appel (une seule implémentation de la classe sous-jacente) alors que d'autres classes sont ensuite chargées et rendent obsolète cette optimisation car elles implémentent cette même méthode. En effet, le choix de la méthode à appeler dépendra alors de la classe réelle de l'objet sur lequel la méthode sera appelée. Après le démarrage, les optimisations sont plus agressives (que le mode client actuel) comme pour le mode serveur. Ce type de compilation permet ainsi de profiter du meilleur des deux modes.

Garbage collector G1

Pour la JVM de Sun, une nouvelle évolution du Garbage Collector sera introduite dans la version 7 (voir peut-être même dans une version précédente) portant de le doux nom de G1 (ou Garbage First). Sans entrer dans trop de détails, le principe est de diviser l'espace mémoire en zones de taille fixe qui seront taggées avec leur génération (jeune ou ancienne, G1 reste donc un collecteur générationel au même titre que l'actuel CMS (Concurrent Mark-Sweep)). Après une phase de marquage globale des objets pour déterminer ceux qui sont encore en vie, le collecteur pourra vider (en copiant les objets d'une zone à une autre) les zones avec peu d'objets vivants afin de les regrouper. Ceci aura pour gain de faire gagner de l'espace dans la heap et supprimer la fragmentation dont souffrait CMS. En libérant plus d'espace mémoire, le passage d'un nettoyage complet (et coûteux en resources CPU) s'effectuera moins souvent.

Types et génériques

Un certain nombre d'évolutions concernent les types Java :

  • l'inférence de types,
  • les annotations sur les types,
  • les types littéraux
  • la réification par des génériques

Afin de décrire plus en profondeur chacune de ces évolutions et des notions qu'elles traitent, un article à part entière abordera ces sujets.

Closures et blocs de gestion automatique des ressources

Les closures, fonctionnalité qui revient sur le devant de la scène avec les langages dynamiques, sont proposées pour être incluses dans Java 7. Nous verrons dans un prochain article ce qu'elles sont et ce qu'elles peuvent apporter au langage Java.

Au cours de ces deux articles nous avons largement détaillé les nouveautés de Java 7. Les points que nous n'avons pas abordés tels la modularité, la gestion de la concurrence et du parallélisme, les évolutions des génériques et le closures seront étudiés dans de futur articles.

Il reste vrai que de nombreuses incertitudes entourent encore cette version de java tant sur sa date de sortie que sur son contenu. Mais au final, nous trouvons que les fonctionnalités pressenties apportent de la valeur à cette plate-forme. Pour notre part, nous retiendrons plus particulièrement :

  • La prise en compte de la modularité et du packaging
  • Des librairies qui facilitent le code de tous les jours avec API Date & Time, Unit & Quantites ou JMX 2.0. Elles sont largement basées sur l'expérience de framework réel, ce qui est un gage de qualité et d'intérêt des concepts importés.
  • Des outils pour faciliter l'utilisation de swing comme Beans Binding et Beans Validation
  • Des améliorations pour rendre le langage plus expressif comme la nouvelle syntaxe pour les propriétés, les méthodes d'extension, ou le support natif du XML. Cela contribue à rendre Java plus léger et moins verbeux pour rester compétitif par rapport à des langages plus jeunes qui ont simplifié leur syntaxe comme JRuby ou C#.
  • Des améliorations techniques par exemple au niveau du garbadge collector ou de l'instruction invokedynamic

Toutes ces modifications même très techniques restent importantes car elles constituent la base de l'innovation pour tous les développements du monde java.

Par David Rousselie et Marc Bojoly