Aller au contenu

Journal

  • Leçon 1 — Le JDK et les outils de build
  • Leçon 2 — Types, égalité et opérateurs
  • Leçon 3 — Classes, records et enums
  • Leçon 4 — Génériques et effacement de type
  • Leçon 5 — Exceptions, null et Optional
  • Leçon 6 — Lambdas et interfaces fonctionnelles
  • Leçon 7 — Collections et Streams
  • Leçon 8 — Pattern matching
  • Leçon 9 — Concurrence et threads virtuels
  • Leçon 10 — Maven et Gradle en profondeur
  • Leçon 11 — Tests
  • Leçon 12 — La bibliothèque standard du quotidien
  • Leçon 13 — La JVM à l’exécution
  • Leçon 14 — Annotations, réflexion et modules
  • Chaîne d’outils sur ma machine : OpenJDK 25+36, Maven 3.9.16, Gradle 9.7.1, et le SDK .NET 10 pour le côté C#. La CI utilise Eclipse Temurin 25 sous Windows, Ubuntu et macOS.
  • Le code du cours se trouve dans code/java-for-csharp :
    • la sortie de chaque exemple est comparée à la leçon par un test JUnit ;
    • chaque extrait rejeté est compilé via l’API javax.tools et doit produire la clé de diagnostic javac que cite la leçon (par exemple compiler.err.not.exhaustive) — l’équivalent Java des codes E0xxx de Rust ;
    • les solutions des exercices sont des tests ;
    • un petit programme .NET 10 affiche le côté C# de chaque comparaison.

Surprises en venant de C# :

  • Integer a = 128, b = 128; a == b vaut false, mais avec 127 c’est true. L’intervalle de cache de −128 à 127 n’est pas un détail d’implémentation : la JLS l’impose.
  • java -jar sur un JAR Maven fraîchement construit échoue avec no main manifest attribute. Rien dans dotnet build ne prépare à un artefact qui ne connaît pas son propre point d’entrée.
  • Un enum C# accepte (Size)42 ; un enum Java ne peut pas contenir une valeur qu’il ne déclare pas. Les enums Java sont bien plus proches d’un ensemble scellé d’objets singletons.
  • Les messages d’erreur de javac sont parfois indirects : créer une classe interne depuis une méthode statique donne non-static variable this cannot be referenced from a static context.

Ce que j’ai d’abord mal fait :

  • Dans un commentaire de code, j’avais décrit final var comme « une variable locale readonly de C# ». C# n’a pas de variables locales readonly. Le commentaire le dit désormais.
  • Je m’attendais à ce que javac -XDrawDiagnostics et l’API du compilateur rapportent la même clé de diagnostic. Ce n’est pas toujours le cas : pour un ajout dans une List<? extends Number>, la ligne de commande rapporte compiler.err.cant.apply.symbols tandis que l’API rapporte la version simplifiée compiler.err.prob.found.req. Les tests utilisent l’API, les leçons citent donc ce que voit l’API.
  • Plusieurs leçons montraient d’abord un extrait d’un snippet rejeté à côté d’un message d’erreur dont le numéro de ligne se rapportait au fichier entier. Les leçons montrent désormais le fichier complet.
  • Le signe d’un exemple s’affichait dans la console Windows. L’exemple écrit désormais « per mille ».

Chaque cas applique une leçon à un dépôt public, à un commit figé.

GuitarAlchemist/ga — le build du plugin JetBrains (leçon 1)

Section intitulée « GuitarAlchemist/ga — le build du plugin JetBrains (leçon 1) »

jetbrains-plugin/ à ga@5560b883 est un projet Gradle en Kotlin DSL (le plugin lui-même est écrit en Kotlin). Je l’ai copié et j’ai essayé de le compiler sur cette machine.

  • Le wrapper est incomplet. Seul gradle/wrapper/gradle-wrapper.properties est versionné. gradlew, gradlew.bat et gradle-wrapper.jar manquent : ./gradlew n’existe donc pas et chaque contributeur doit installer un Gradle compatible. gradle wrapper régénère les trois fichiers, qu’il faudrait versionner.

  • Gradle 8.5 ne peut pas tourner sur JDK 25. Avec le Gradle 8.5 figé et JDK 25, gradle help échoue avec un message d’un seul mot :

    * What went wrong:
    25

    La trace de pile montre une java.lang.IllegalArgumentException: 25 levée pendant que Gradle compile le script de build Kotlin : le compilateur Kotlin embarqué dans cette version de Gradle ne connaît pas Java 25. La correction passe soit par un JDK que Gradle 8.5 prend en charge, soit par un Gradle plus récent ; la matrice de compatibilité de Gradle indique quelle version de Gradle tourne sur quelle version de Java.

  • Mettre à jour Gradle seul ne suffit pas. Avec Gradle 9.7.1, le build va plus loin et échoue dans l’ancien plugin org.jetbrains.intellij 1.16.1 :

    class org.jetbrains.intellij.MemoizedProvider overrides final method org.gradle.api.internal.provider.AbstractMinimalProvider.toString()Ljava/lang/String;

    Ce plugin a été remplacé par l’IntelliJ Platform Gradle Plugin 2.x, qui utilise un DSL différent. La migration est un vrai changement, pas une simple montée de version.

  • Le dossier de cache de Gradle est versionné. Douze fichiers sous jetbrains-plugin/.gradle/ (fichiers de verrou, sommes de contrôle, last-build.bin) sont dans le dépôt, l’équivalent Gradle de versionner obj/. Il a sa place dans le .gitignore.

  • Une cible Java sans toolchain. Le script définit sourceCompatibility = "17" sur chaque tâche JavaCompile au lieu d’un bloc java { toolchain { … } } : le JDK utilisé dépend donc de la machine qui exécute Gradle — exactement la variabilité que décrit la note sur les toolchains de la leçon 1.

Étape suivante, à vérifier : générer le wrapper, migrer vers le plugin 2.x, et vérifier que le plugin se charge toujours dans une version actuelle d’IntelliJ IDEA.

spareilleux/learn — l’exemple Spring Boot du cours WSL (leçon 1, exercice 2)

Section intitulée « spareilleux/learn — l’exemple Spring Boot du cours WSL (leçon 1, exercice 2) »

code/wsl-containers/java-reactor-api n’a aucune configuration de maven-jar-plugin, et pourtant java -jar target/app.jar fonctionne. Le compiler montre pourquoi :

Main-Class: org.springframework.boot.loader.launch.JarLauncher
Start-Class: dev.learn.reactorapi.JavaReactorApiApplication

Le spring-boot-maven-plugin réempaquette le JAR : la Main-Class du manifeste est le lanceur de Spring Boot, qui lit Start-Class et charge les 62 JAR de dépendances imbriqués sous BOOT-INF/lib/ (35 Mo au total). Il répond aux deux moitiés de l’exercice 2 — le point d’entrée manquant et les dépendances manquantes. L’élément <parent> (spring-boot-starter-parent) explique aussi pourquoi le POM n’indique aucune version de plugin ni de dépendance : il joue le rôle de Directory.Packages.props et des valeurs par défaut du SDK. La leçon 10 y reviendra.

  • Maven 4 vaut-il déjà la peine d’être enseigné, alors que la plupart des projets utilisent encore la 3.9 ? À vérifier : son statut de publication.
  • Comment les inspections d’IntelliJ IDEA se comparent-elles à -Xlint:all de javac pour les pièges des leçons 2 et 3 ?