Java 23 está ahora disponible

Esta semana que recién finaliza Oracle lanzó Java 23. La versión más reciente de la JDK llega con un importante número de novedades que como desarrollador deberías conocer, eso si, la mayoría de las cosas llegan en “Preview”. Fueron presentadas al menos 7 interesantes JDK Enhancement Proposal (JEP) en esta nueva versión que no es LTS por lo que no está recomendada para entornos productivos. Por otro lado al estar muchas de las novedades en fase de preview tampoco se recomiendan para comenzar a usar.

Sin embargo es importante estar al día, porque lo que hoy sale como preview en una versión no LTS es muy probable en la próxima LTS (que será Java 25, en el 2025) sea ya una feature definitiva de Java.

Veamos entonces que se está cocinando.

Nuevas JEP en Java 23 (Versiones finales)

Comentarios de la documentación de Markdown (JEP 467). Es una característica definitiva en versión final y nos da la capacidad de escribir comentarios de JavaDoc utilizando Markdown, lo que hace que la documentación final sea más legible y más fácil de escribir; la intención es que documentemos más 😊.

Usarlo es simple, solo basta con anteponer tres slash a la línea de documentación, el generador de Java doc lo entenderá como markdown.

  /// More info [SACAViX](http://sacavix.com) blog
    // JEP 467
    public static void main(String[] args) {
        System.out.println("Sample Markdown");
    }

Así se vera en la documentación de Java.

ZGC usará el modo generacional por defecto (JEP 474). También como feature final el modo predeterminado para el recolector de basura Z será generacional, esto brinda la ventaja de la recolección más frecuente de objetos jóvenes para aumentar el rendimiento de la aplicación. El modo no generacional quedará marcado como obsoleto para su posterior eliminación.

Nuevas JEP en Java 23 (En preview)

Concurrencia estructurada (JEP 480, tercera preview): tiene como objetivo simplificar la programación concurrente mediante el tratamiento de las tareas relacionadas de varios subprocesos como una sola unidad de trabajo. Esto facilita el manejo de errores y la observabilidad, lo que reduce riesgos al trabajar con hilos (threatleaks) y permite la cancelaciones. Sobre este tema en particular hicimos en video explicando con ejemplos, puede verlo en nuestro canal de Youtube.

Valores con ámbito (JEP 481, tercera preview): los Scoped Values facilitan el uso compartido de datos inmutables dentro de subprocesos y con subprocesos secundarios. Scoped Values es como ThreadLocal pero enfocado en los hilos virtuales, resolviendo el problema de la propagación de contexto (dentro del ámbito) entre hilos que hoy tiene ThreadLocal.

Veamos un ejemplo:

   ExecutorService executor = Executors.newFixedThreadPool(2);

            Runnable task = () -> ScopedValue.where(scopedValue, "Value in " + Thread.currentThread().getName())
                    .run(() -> {
                System.out.println(Thread.currentThread().getName() + " initial value: " + scopedValue.get());

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    System.out.println("Interrupted while waiting for value");
                }
                System.out.println(Thread.currentThread().getName() + " final value: " + scopedValue.get());
            });

            executor.submit(task);
            executor.submit(task);
            executor.shutdown();

La salida para el código anterior:

pool-1-thread-1 initial value: Value in pool-1-thread-1
pool-1-thread-2 initial value: Value in pool-1-thread-2
pool-1-thread-1 final value: Value in pool-1-thread-1
pool-1-thread-2 final value: Value in pool-1-thread-2

Declaraciones de importación de módulos (JEP 476, preview). Permite importar módulos completos con una sola declaración, lo que simplifica el uso de bibliotecas modulares. Saca ventaja del proceso de modularizarían que se inicio en Java 9 y ahora es posible importar módulos enteros que hayan sido previamente exportados.

Ejemplo:

import module java.base;

El código anterior importa todo lo exportado en java.base, si nos metemos dentro sería:

module java.base {
    exports java.io;
    exports java.lang;
    exports java.lang.annotation;
    exports java.lang.classfile;
   //----- Y otros cientos de paquetes 
}

Tipos primitivos en patterns, instanceof y switch (JEP 455, Preview). Esto elimina las restricciones en los tipos primitivos cuando se trabaja con coincidencia de patrones, instanceof y switch, lo que permite que Java sea más expresivo y un lenguaje mas simple de usar.

Un ejemplo sería:

 /// Bad code, just a sample
    // JEP 455
    public static String getDayOfWeek(int day){
        return switch (day) {
            case 1 -> "Sunday";
            case 2 -> "Monday";
            case 3 -> "Tuesday";
            case 4 -> "Wednesday";
            case 5 -> "Thursday";
            case 6 -> "Friday";
            case 7 -> "Saturday";
            default -> "Unknown";
        };
    }

    public static boolean isInt (Object number) {
        return number instanceof int;
    }

Constructores flexibles (JEP 482): esta JEP permite a los constructores inicializar campos antes de invocar a un constructor de la clase padre. Antes se le llamaba también como “declaraciones antes de super”, pero ha sido renombrado en la JEP 482 como constructores flexibles.

Ejemplo:

class PositiveBigDecimal extends BigDecimal {

    // JEP 482 - Constructores flexibles
    public PositiveBigDecimal(Long val) {
        if (val.compareTo(0L) < 0) {
            throw new ArithmeticException("Negative numbers are not allowed");
        }
        super(val);
    }
}

Stream Gatherers (JEP 473). Recopiladores de flujos se introdujo en la JDK 22 e implican una mejoran la API de streams para admitir operaciones intermedias personalizadas. Permiten que las tuberías de los streams transformen los datos de formas que no se pueden lograr con facilidad con las operaciones intermedias que existen ahora. Dedicaremos un video futuro en nuestro canal de Youtube a este tema en el que profundizaremos. A continuación te comparto un ejemplo básico.

    // JEP 473 Gatherers
    public static void main(String[] args) {
        List<List<Integer>> sample =  Stream.iterate(0, i -> i + 2)
                .gather(Gatherers.windowFixed(2))
                .limit(5)
                .collect(Collectors.toList());

        System.out.println(sample);
    }

Salida para el ejemplo anterior:

[[0, 2], [4, 6], [8, 10], [12, 14], [16, 18]]

Hemos usado un Gatherer predefinido que tiene la capacidad de generar listas fijas de tamaño 2 en una operación intermedia del stream, limitado a 5 elementos.

Java 23 (deprecada)

Se han declarado como obsoletos los métodos de acceso a la memoria en el paquete sun.misc.Unsafe y sera movido en el futuro. El reemplazo para sun.micUnsafe fue introducido en Java 9 con VarHandle API y en 22 se complemento con Foreign Function y Memory API.

También fue removido el trabajo que se venia haciendo con String Templates o plantillas de cadenas. Los resultados de los previews anteriores no fueron los esperados y se realizara un rediseño en una nueva JEP.

Documentación renovada

Por último y no menos importante la documentación generada con Java doc ahora viene con nuevo look, como se muestra en la siguiente imagen. Ahora tendremos panel lateral para la evagación y por supuesto, markdown.

¿Te ha gusto el artículo?

Mira los ejemplos completos en GitHub. Seguí nuestro trabajo en Youtube y apóyalo si deseas.