JEP 390 Warnings for Value Based Classes
Contexte
Avant de parler de ces nouveaux avertissements, présentons le projet Projet Valhalla.
Ce projet travaille notamment sur deux fonctionnalités :
-
"inline type"
-
Generic over Primitive Types.
Le travail concerne l’amélioration de la prise en compte des types primitifs afin de réaliser des optimisations.
Dans le monde Java, tout est objet. Chaque type primitif correspond une classe "wrapper", comme java.lang.Integer
, java.lang.Double
pour respectivement les types int
, double
. La gestion des ensembles Set
ou des listes List
est un cas d’usage. En effet, nous ne pouvons pas écrire la définition suivante : Set<int>
.
Un autre cas d’usage est l’utilisation de ces classes dans les entités JPA qui permet de traiter la valeur nulle d’une colonne. En effet, en utilisant le type primitif, la colonne devient obligatoire.
Cependant, cela a un coût en mémoire et un coût en temps d’accès. Pour accèder à la valeur, il faut passer par référence qui demande un accès mémoire. Par exemple, un tableau d’entier int[]
prend moins de places qu’un Set<Integer>
. Sans compter que côté matériel, l’accès d’un tableau est plus efficace qu’un parcours d’un ensemble dont il est nécessaire d’accèder à la valeur par référence. En effet, cela nécessite un accès mémoire donc les caches et les pipelines des processeurs ne sont pas mis à profit.
De plus, le fait qu’ils soient des instances de classes, ils portent une identité qui n’a pas de sens pour les types primitives.
Value-based classes
Les "Value based classes" sont des classes qui ont les propriétés suivantes :
-
sont des classes finales et immutables,
-
possèdent les méthodes :
equals()
,hascode()
ettoString()
, -
l’égalité en utilisant la méthode
equals()
et non l’opération d’égalité (==
), -
n’ont pas de constructeurs mais une méthode fabrique "factory method" qui n’apporte aucun garantie sur l’identité.
L’impact est assez important. Donc, la transition se fait par étape. s
La première a été de désigner les classes candidates. Nous avons par exemple :
-
les classes "wrapper" des types primitives :
java.lang
(Byte
,Short
,Integer
,Long
,Float
,Double
,Boolean
etCharacter
) -
les classes optionnelles :
java.util
(Optional
,OptionalInt
,OptionalLong
etOptionalDouble
) -
certaines classes de l’API
java.time
(Instant
,LocalDate
,LocalTime
,LocalDateTime
,ZonedDateTime
,ZoneId
,OffsetTime
,OffsetDateTime
,ZoneOffset
,Duration
,Period
,Year
,YearMonth
etMonthDay
)
La seconde étape a été de déprécier les contructeurs des classes "wrapper" dans le JDK 9.
Les classes optionnelles`java.util` et les les classes de l’API java.time
ne possèdent pas de constructeurs. Ils utilisent le principe des méthodes de fabriques.
ZonedDateTime maintenant = ZonedDateTime.now();
OptionalInt option = OptionalInt.of(15);
Warnings for Value Based Classes
L’objectif de la troisième étape est de décourager les mauvaises utilisations de ces classes en ajoutant des avertisssements à la compilation et à l’exécution.
Dans le JDK 16, les constructions des classes "wrapper" sont dépréciés pour suppression.
warning: [removal] Double(double) in Double has been deprecated and marked for removal
d = new Double(20.0);
^
Pour rappel, nous avons deux posssibilités
Double d = Double.valueOf(10.0); // factory method
Double d = 10.0; // tout simplement :)
Une contrainte supplémentaire est qu’il ne sera plus possible d’utiliser ces classes pour les blocs synchronized
. C’est pourquoi, l’objectif est d’ajouter un avertissement pour déconseiller cet usage.
Double d = 10.0;
synchronized (d) {
System.out.println("Section synchronized (d)");
}
Lors de la compilation, nous obtenons le message suivant :
warning: [synchronization] attempt to synchronize on an instance of a value-based class
synchronized (d)
^
Cependant, en fonction du code, cela ne peut pas être détecter lors de la compilation.
Double d = 10.0;
Object o = d;
synchronized (o) {
System.out.println("Section synchronized (o)");
}
En effet, la définition d’un bloc synchronized
avec la classe Object reste naturellement autorisée. Donc le compilateur n’affiche pas d’avertissement.
C’est pourquoi, des avertissements à l’exécution ont aussi été implémentés. Par contre, ces avertissements ne sont pas activés par défaut. Il faut ajouter l’option suivante sur la ligne :
-
-XX:DiagnoseSyncOnValueBasedClasses=1
Cela est plus qu`un avertissement puisque la JVM s’arrête avec une erreur fatale. -
-XX:DiagnoseSyncOnValueBasedClasses=2
Celui-ci est un avertissement sur la console ou via les événements JDK Flight Recorder.
[0,046s][info][valuebasedclasses] Synchronizing on object 0x000000071721b3b0 of klass java.lang.Double
[0,046s][info][valuebasedclasses] at fr.lbenoit.billets.codes_sources.Programme.main(Programme.java:16)
[0,047s][info][valuebasedclasses] - locked <0x000000071721b3b0> (a java.lang.Double)
Section synchronized (d)
[0,047s][info][valuebasedclasses] Synchronizing on object 0x000000071721b3b0 of klass java.lang.Double
[0,047s][info][valuebasedclasses] at fr.lbenoit.billets.codes_sources.Programme.main(Programme.java:22)
[0,047s][info][valuebasedclasses] - locked <0x000000071721b3b0> (a java.lang.Double)
Section synchronized (o)
Il est à noter que pour utiliser ces options, il faut activer les diagnostics VM avec l’option -XX:+UnlockDiagnosticVMOptions
C’est l’occasion de se préparer au futur de Java.
Moteur de recherche
"Eduquer, ce n'est pas remplir des vases mais c'est d'allumer des feux." - Michel Montaigne
Billets récents
- Eclipse plante systématiquement sous Debian (et autres distribution Linux)
- JEP 463, Implicitly Declared Classes and Instance Main Methods (Second Preview)
- Debian - Montée de version de Debian 11 (Bullseye) à Debian 12 (Bookworm)
- JEP 451, Prepare to Disallow the Dynamic Loading of Agents
- JEP 444, Virtual Threads