LCC 255 - Interview Java 16 avec José Paumard et Henry Tremblai - partie 1

25 views
Skip to first unread message

Emmanuel Bernard

unread,
May 10, 2021, 10:42:45 AM5/10/21
to lescast...@googlegroups.com
José (maintenant Java Advocate chez Oracle – le cachotier) et Henri échangent avec Emmanuel sur la sortie de Java 16.
Cette première partie couvre l'introduction, les records, le pattern matching, les sealed class, les hidden classes et le créateur d'installeur jpackage.
La suite au prochain numéro.


Emmanuel

Remi Forax

unread,
May 10, 2021, 3:19:05 PM5/10/21
to lescastcodeurs
Long mail, désolé, j'ai tapé en écoutant l'épisode.

José:
  Pour les records, le comportement de toString, equals et hashCode est pas définie par le compilo mais par le JDK,
  par exemple pour toString, le compilo génére juste un invokedynamic et demande au JDK en fait à la classe java.lang.runtime.ObjectMethods [1] comment faire le boulot.
  Cela veut dire que l'on peut changer le comportement des equals/toString/hashCode sans recompiler les records.
 
  Entre autre, on sait que dans le futur, on aimerait bien changer le code de hashCode car le hashCode actuel a trop de collisions mais personne est vraiment d'accord sur quel autre fonction utilisée donc cela traine un peu.
 
Le deconstructeur, c'est l'inverse du constructeur
  class Point {
    Point(int x, int y) { // constructeur
    (int x, int y)  deconstructor() { // deconstructeur   SYNTAX PAS FINALISEE !
  }
 
cela permet d'écire des truc du genre
  switch(o) {
    case Point(var a, var b): // équivalent à instanceof Point + appel du deconstructeur, donc 'a' contient la valeur 'x' du Point et 'b' la valeur 'y'.  
  }
 
Un deconstructeur est un accesseur sous-steroid, comme un accesseur il permet l'encapsulation, l'API public et les champs privées sont bien séparés, et en plus tu accédes à toutes les valeurs en 1 appel au lieu de devoir faire plusieurs get*.
 
Henry,
  record != primitive class,
  Tu confonds les records et les primitives classes (de Valhalla).
  Les primitives classes (et primitive records) ne sont pas alloué dans le tas mais sur la pile (en vérité dans des registres si possible)
   
  La seule différence entre un record et une classe et effectivement que tu peux pas changé la valeur des champs par réflection (même avec un setAccessible) et que en terme de performance si tu as un record dans un champ static final, alors les champs du record sont aussi considérés comme static final.
   
  Pour les proxy sur un records, avec Objgenesis tu dois pouvoir créer un record avec les champs vides (au moins pour l'instant) mais tu peux pas changer les valeurs des champs. DBref pas très utile.
 
Henry,
  avoir une static factory sur record sert pas un grand chose, un record est censé être un objet sans encapsulation, si tu veux de l'encapsulation alors c'est pas un record.
  Le constructeur d'un record doit avoir au moins la même visibilité que le record lui-même car la de-serialization d'un record utilise le constructeur contraieremnt à la dé-serialization classique, donc ton constructeur doit être aussi visible que le record.  
   
Pour sealed:
  Sealed class/interface est repompé de Scala (puis Kotlin), c'est pas vraiment un truc nouveau,
  ça permet de lister tous les sous-types directs.
   
  Cela permet au compilo de savoir quelles sont tous les types quand on fait du pattern matching
  et cela permet aussi d'être sûr que quand on dit JSONData = JSONObject | JSONObject | JSONArray,
  personne ne peux aller ajouter un JSONMonObjetAMoi.
   
  En terme de test, cela fait moins de test à écrire, pas besoin de tester ce qui se passe si on a une classe
  qui imlante l'interface JSONData pas correctement (car c'est pas possible).
   
  Pour l'instant, sealed touche ni le système de typage, ni aux optimisations de la VM (pas encore),
  le compilo et la VM check juste que lorsque l'on charge un sous-type direct,
  il fait parti de la liste des types autorisés.
     
Pour le polymophisme:
  La raison de pourquoi c'est limité à deux implantation pas défaut (bimporphic donc), c'est que si tu veux plus, il faut plus de mémoire pour stocker tous les cas possibles (et cela pour chaque appel de méthode) et surtout tu vas devoir générer beaucoup plus de codes assembleur car tu as plus de cas à gérer, donc le code généré risque de devenir trop gros (ce qui est un vrai problème en assembleur).
   
  Et pour Emmanuel, tu peux changer la valeur de "morphicité" avec -XX:TypeProfileWidth=8 (cf [2])
 
Hidden class,
  on veut créer des classes de "glue"
   - qui peuvent accéder aux champs (même privée) de la classe principale et des classes internes,
     la hidden class et la classe principale sont potes (nestmate)
   - qui ne sont pas visibles dans le stacktrace
    
  En Java 8, on utilisait des anonymous classes pour cela, mais les anonymous classes ont deux problèmes,
  c'est un gros hack en terme de sécurité basiquement cela dit, fait semblant que je suis cette autre classe là
  et les anonymous classes ont jamais été définie dans la JVMS, donc il faut utiliser Unsafe et
  certaines VMs ne les ont jamais implantées.
   
  Donc l'idée des hidden classes, c'est d'avoir une belle implantation qui marche quelque soit la VM,
  sans que l'on ait besoin de passer par Unsafe.
   
  L'autre truc important est que l'on peut choisir si on veut que la hidden class soit indépendante du class loader ou pas. On peut se dire que c'est chouette d'être indépendant du ClassLoader mais cela veut dire que la hidden class va avoir son propre meta space, et un metaspace, c'est pas petit, donc si tu as plein de hidden classes, si elles ont chacun leur propre metaspace c'est pas un très bonne idée en terme de consommation de metaspace. Les lambdas avant Java 15 avaient se problème.
 
Voilà, voilà,
Rémi



--
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes "lescastcodeurs".
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse lescastcodeur...@googlegroups.com.
Cette discussion peut être lue sur le Web à l'adresse https://groups.google.com/d/msgid/lescastcodeurs/CAEW2Rj%2Bo5CEJOBCXsyCvqaF2bFPnvTffixDooB6vf0utLZL%3DjQ%40mail.gmail.com.

Henri Tremblay

unread,
May 10, 2021, 4:18:20 PM5/10/21
to lescastcodeurs
Pfff.... M. Bernart fait encore des siennes. 

--

Henri Tremblay

unread,
May 10, 2021, 4:32:07 PM5/10/21
to lescastcodeurs
Merci pour les précisions.
J'ai peut-être pas été clair mais oui, records != primitive. Et oui pour la pile. Mais il y avait un lien.

La factory statique, c'est parce que c'est jolie.
Par exemple, LocalDate, dans ma tête, ça pourrait être un record. Et il n'y a pas de constructeur public.

Et justement, quand on avait eu cette discussion, tu m'as dit un truc comme "Non, c'est plutôt un type XX qui englobe les types primitifs" Et les primitive records sont donc un sous-ensemble de records, mais primitif en plus. Ce qui pour l'instant fait un jolie diagramme de Venn dans ma tête.

Le reste, c'est pas mal ça, j'ai pas trop dis de niaiseries :-)

Remi Forax

unread,
May 10, 2021, 5:13:12 PM5/10/21
to lescastcodeurs
De: "Henri Tremblay" <henri.t...@gmail.com>
À: "lescastcodeurs" <lescast...@googlegroups.com>
Envoyé: Lundi 10 Mai 2021 22:31:52
Objet: Re: [LCC] LCC 255 - Interview Java 16 avec José Paumard et Henry Tremblai - partie 1
Merci pour les précisions.
J'ai peut-être pas été clair mais oui, records != primitive. Et oui pour la pile. Mais il y avait un lien.

La factory statique, c'est parce que c'est jolie.
Par exemple, LocalDate, dans ma tête, ça pourrait être un record. Et il n'y a pas de constructeur public.

LocalDate est pas un record car tu sais pas exactement comment sont stockée les valeurs. LocalDate utilise l'encapsulation pour te dire, en fait ce que je stocke tu t'en fous pourvu que les méthodes marchent.
Un record a pas d'encapsultation, ce que tu passes au constructeur est stocké sous forme de champs, tu as juste le droit d'ajouter des préconditions, genre requireNonNull, etc,
avec un record tu dit je suis un "String name x int age que l'on appel Person", c'est l"équivalent d'un type produit en fonctionnel [1] mais comme on est en Java, on ajoute des noms car c'est mieux d'avoir une erreur qui dit que la variable est pas de type Person plutot que la variable est pas de type String x int, même si conceptuellement c'est la même chose.


Et justement, quand on avait eu cette discussion, tu m'as dit un truc comme "Non, c'est plutôt un type XX qui englobe les types primitifs" Et les primitive records sont donc un sous-ensemble de records, mais primitif en plus. Ce qui pour l'instant fait un jolie diagramme de Venn dans ma tête.

Ce que propose Valhalla c'est d'étendre la notion de primitive pour que tu puisses utiliser tes propres classes primitives pas que int, long, etc
Donc c'est pas exactement un digramme de Venn c'est plus un tableau 2x2, tu as les record et les pas record, les primitives et les pas primitives, donc 4 combinaisons possibles.



Le reste, c'est pas mal ça, j'ai pas trop dis de niaiseries :-)

Ouvrir la trappe c'est dire de niaiseries, c'est pas un proverbe québéquois ?

Rémi


Henri Tremblay

unread,
May 10, 2021, 6:16:23 PM5/10/21
to lescastcodeurs
Ok. Mais on est d'accord qu'un LocalDate c'est un primitif? Comme dans "J'aimerai vraiment ça qu'il passe sur la stack celui-là"

Remi Forax

unread,
May 11, 2021, 2:54:28 AM5/11/21
to lescastcodeurs



De: "Henri Tremblay" <henri.t...@gmail.com>
À: "lescastcodeurs" <lescast...@googlegroups.com>
Envoyé: Mardi 11 Mai 2021 00:16:10
Objet: Re: [LCC] LCC 255 - Interview Java 16 avec José Paumard et Henry Tremblai - partie 1
Ok. Mais on est d'accord qu'un LocalDate c'est un primitif? Comme dans "J'aimerai vraiment ça qu'il passe sur la stack celui-là"

oui, en fait, c'est un primitif un peu particulier car il accept null, c'est ce que l'on appel un "value based class", qui correspond à un primitif nullable.
Si tu lis la javadoc [1], c'est déjà prévu, il reste plus qu'à l'implanter les optimisations, c'est pas super facile car le code doit être backward compatible donc on est assez limité, mais comme on veut aussi la même optimisation pour Integer ou Optional, on a pas le choix, il faut que cela marche.

C'est pour cela qu'il y a la JEP 390 (intégré à Java 16) qui demande au compilo d'émettre des warnings quand tu fais des synchronized sur des Integer, Optional, LocalDate, etc et t'engueules quand tu utilises new Integer() au lieu de Integer.valueOf(), car ces opérations ne marcheront plus lorsque Integer ou LocalDate seront des classes primitives.

Rémi



Henri Tremblay

unread,
May 11, 2021, 8:26:14 AM5/11/21
to lescastcodeurs
On parle de JEP 390 dans la partie 2. Suis super content de celle-là :-)
Et oui, pour Optional c'est super important.

Emmanuel Bernard

unread,
May 11, 2021, 4:36:31 PM5/11/21
to lescast...@googlegroups.com
C'est la loose parce que en plus j'ai fait spécialement attention. Et paf, le titre != du corps du texte... J'ai corrigé sur le site.

Henri Tremblay

unread,
May 11, 2021, 5:46:05 PM5/11/21
to lescastcodeurs
:-) Tu es pardonné. Va te falloir un redirect du lien html :-D

Matthieu Baechler

unread,
May 13, 2021, 4:59:19 AM5/13/21
to lescast...@googlegroups.com
Salut les CastCodeurs,

Merci pour cet épisode et tous les précédents.

Je note que vous avez une lecture de la programmation biaisée par votre historique en Java et que sur certains points, vous manquez de pratique en programmation fonctionnelle pour comprendre l'évolution des technologies en général et de Java en particulier.
Je pense que vous devriez associer une personne qui serait capable d'apporter cet angle à vos podcast.

Pour illustrer mon propos, sur le sujet des sealed et du Pattern Matching, c'est comme le dit Rémi repris de Scala (qui l'a lui-même repris de langages fonctionnels antérieurs) dans une optique bien particulière.

Le design objet chercher à définir des hiérarchies de classe pour écrire des algorithmes qui fonctionnent si l'on ajoute de nouveaux types.
Pour reprendre le cas des formes, si un forme a une fonctionne surface, alors il suffit que ma nouvelle forme implémente `surface` pour que mon algorithme puisse fonctionner. L'algorithme n'a pas besoin d'une connaissance préalable de la forme exacte.

Ce type de programmation rend l'ajout de nouveaux types facile. En revanche, ajouter une nouvelle méthode à `forme` signifie ajouter cette méthode à toutes les implémentations ce qui est coûteux.

La combinaison sealed + Pattern Matching c'est l'inverse: ajouter un type revient à aller mettre à jour toutes les fonctions qui font un Pattern Matching pour traiter le nouveau cas. En revanche, ajouter une nouvelle fonction est assez simple.

Ce n'est qu'un exemple parmi d'autres et je ne prétends pas être expert du domaine, mais je pense que les CastCodeurs devraient prendre le virage de la programmation fonctionnelle qui prend une place de plus en plus significative dans le métier.

Merci encore pour votre podcast,

-- Matthieu Baechler

--

Remi Forax

unread,
May 13, 2021, 8:34:40 AM5/13/21
to lescastcodeurs
De: "Matthieu Baechler" <matthieu...@gmail.com>
À: "lescastcodeurs" <lescast...@googlegroups.com>
Envoyé: Jeudi 13 Mai 2021 10:59:07
Objet: Re: [LCC] LCC 255 - Interview Java 16 avec José Paumard et Henry Tremblai - partie 1
Salut les CastCodeurs,

Salut Matthieu,


Merci pour cet épisode et tous les précédents.

Je note que vous avez une lecture de la programmation biaisée par votre historique en Java et que sur certains points, vous manquez de pratique en programmation fonctionnelle pour comprendre l'évolution des technologies en général et de Java en particulier.
Je pense que vous devriez associer une personne qui serait capable d'apporter cet angle à vos podcast.

Pour illustrer mon propos, sur le sujet des sealed et du Pattern Matching, c'est comme le dit Rémi repris de Scala (qui l'a lui-même repris de langages fonctionnels antérieurs) dans une optique bien particulière.

yep, les dérivés de ML [1], donc par ordre de création (ou de repompage) Standard ML, Caml (le ML français, cocorico !) et F#.

Une des idées est de modélisé le monde avec 2 constructions principales,
- les product-types, qui sont des tuples, par exemple, une voiture à un conducteur et une plaque d'immatriculation,
     type Car = String * String
- les sum-types, qui est un "ou" entre plusiuers types,
    type Vehicle = Car | Bus

Comme ces types sont basés sur des compositions d'autres types, on les appel des types algébriques, algèbre veut dire réunion de différentes parties, enfin c'est ce qu'à dit mon premier prof de Math à la fac.

Et on peut faire du pattern matching sur des types algébriques (ADT en anglais, pour algebraic datatype)
    fun licencePlate (Car(owner, plate)) = plate
       |  licencePlate (Bus(plate)) = plate

En Scala, Kotlin ou Java,
- les product-types sont des data classes, records, on peut noter que Java est plus proche du concept original car un record est non mutable,
- les sum-types dont des interfaces sealed, des interfaces qui liste tous ces sous-types directs.

  sealed interface Vehicle {
      record Car(String owner, String plate) implements Vehicle {}
      record But(String plate) implements Vehicle {}
  }
 
et on veut faire du pattern matching
  String licencePlate(Vehicle vehicle) {
    return switch(vehicle) {
      case Car(_, var plate) -> plate;
      case Bus(var plate) -> plate;
    };
  }


Le design objet chercher à définir des hiérarchies de classe pour écrire des algorithmes qui fonctionnent si l'on ajoute de nouveaux types.
Pour reprendre le cas des formes, si un forme a une fonctionne surface, alors il suffit que ma nouvelle forme implémente `surface` pour que mon algorithme puisse fonctionner. L'algorithme n'a pas besoin d'une connaissance préalable de la forme exacte.

Ce type de programmation rend l'ajout de nouveaux types facile. En revanche, ajouter une nouvelle méthode à `forme` signifie ajouter cette méthode à toutes les implémentations ce qui est coûteux.

C'est ce que l'on appel l' "expression problem" [2], enfin c'est Phillipe Wadler qui propose de l'appeler comme cela en théorisant que l'on peut classer les différents langages de programmation typé statiquement en fonction de leur réponse à l'expression problem.
Comme tu le dis, les langages à base d'interface sont extensibles en terme d'implantations, les langages à base de type algébriques sont extensibles en terme de fonctions.

Il y a papier très connu [3] de Wadler et Odersky (Le papa de Scala) qui en 1998 explique comment ajouter les types paramétrés, les lambdas et le pattern matching à Java, c'est ce qui sert de feuille de route informelle pour Java.
Tous les 10 ans, on a une mini-révolution qui ajoute une des fonctionnalités, 2004 pour les generics, 2014 pour les lambdas, 2024 pour le pattern matching à Java.


La combinaison sealed + Pattern Matching c'est l'inverse: ajouter un type revient à aller mettre à jour toutes les fonctions qui font un Pattern Matching pour traiter le nouveau cas. En revanche, ajouter une nouvelle fonction est assez simple.

oui,
la raison, je pense, pour laquelle les types algébriques sont intégrés seulement maintenant est que Java est devenu plus un langage de glue (le langage co-évolue avec l'évolution de la façon dont on utilise l'informatique) où utiliser l'encapsulation *partout* est moins intéressant.

En terme d'utilisation,
soit on est coté application et modéliser un domaine métier avec des types algébriques c'est assez sympa, et si/quand les requirements changent, on a pas grand chose à changer
soit on est coté librarie, les libraries sont devenues plus grosse et avec une séparation plus claire entre l'intérieur de la librarie et l'extérieur. Pour la partie intérieur d'une librarie, utiliser des types algébriques fait ausi sense. Pour la partie API, là c'est pas une bonne idée d'utiliser des types algébriques car on veux garder le fait que l'API est définie en terme de types extensibles.



Ce n'est qu'un exemple parmi d'autres et je ne prétends pas être expert du domaine, mais je pense que les CastCodeurs devraient prendre le virage de la programmation fonctionnelle qui prend une place de plus en plus significative dans le métier.

Le problème est que les langages fonctionnels ça existe pas vraiment, il y a pas un seul domaine, on a rétro-activement catégorisé des languages sortie dans les années 80 et 90, comme fonctionnel.
Tous les langages fonctionnels n'utilisent pas des types algébriques, pour prendre des exemples de langages plus connus, Clojure (enfin imagine une version typé de Clojure) ou JavaScript/TypeScript se base sur un système de type structurelle où les datas sont définies partiellement pas sur des types algébriques, pourtant c'est langages sont fonctionnels.

En fait, on a le même problème pour le terme langage object, Python ou JavaScript, on des objets mais pas d'encapsulation, Go et Rust ont des objets mais pas d'héritage, etc. Tous les langages objets ont du polymorphisme, mais cela revient à dire que si un langage à une syntaxe d'appel de méthode genre a.foo(b), c'est un langage objet, et là, on a pas dit grand chose.

Pour moi, faire un podcast avec un expert sur le domaine que Java vampirise une fois tous les 10 ans devrait être suffisant :)


Merci encore pour votre podcast,

-- Matthieu Baechler



On Mon, May 10, 2021 at 2:42 PM Emmanuel Bernard <emma...@lescastcodeurs.com> wrote:
José (maintenant Java Advocate chez Oracle – le cachotier) et Henri échangent avec Emmanuel sur la sortie de Java 16.
Cette première partie couvre l'introduction, les records, le pattern matching, les sealed class, les hidden classes et le créateur d'installeur jpackage.
La suite au prochain numéro.


Emmanuel
--
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes "lescastcodeurs".
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse lescastcodeur...@googlegroups.com.
Cette discussion peut être lue sur le Web à l'adresse https://groups.google.com/d/msgid/lescastcodeurs/CAEW2Rj%2Bo5CEJOBCXsyCvqaF2bFPnvTffixDooB6vf0utLZL%3DjQ%40mail.gmail.com.
--
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes "lescastcodeurs".
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, envoyez un e-mail à l'adresse lescastcodeur...@googlegroups.com.

Henri Tremblay

unread,
May 13, 2021, 11:17:11 PM5/13/21
to lescastcodeurs
Surtout qu'on a l'air nono comme ça (bon, je ne suis pas un cast codeur mais comme j'étais dans l'épisode), mais tu as au moins 2 cast codeurs qui ont travaillé sur des nouveaux langages.

Bien évidemment que beaucoup de features des dernières versions sont là pour aider la programmation fonctionnelle. C'est pas parce qu'on en parle pas qu'on ne sait pas :-) Ni qu'on ignore d'où ça vient. Mais on a déjà fait 2h et le pattern matching est pas encore assez là pour en parler. Le fun arrive dans les prochaines versions.

Reply all
Reply to author
Forward
0 new messages