Novedades de Java 14

Escrito por el .
java planeta-codigo
Enlace permanente Comentarios

Java

Entre las novedades más destacadas que incorpora Java 14 están los records, la incorporación definitiva de las expresiones switch o el pattern matching para el operador instanceof. Otra de las novedades más destacadas es una traza de NullPointerException más útil, también destaca la posibilidad de utilizar el recolector de basura ZGC en Windows y macOS. El resto de novedades son la eliminación de algunas funcionalidades con poco uso y la preparación marcando como desaconsejado su uso con deprecated.

Introducción

Las mejoras incluídas en esta versión son:

Nuevas características

Excepciones NullPointerException más útiles

Cuando se produce una excepción NullPointerException por usar una referencia de objeto cuyo valor es null Java emite una traza indicando la línea de código donde se ha producido, la clase y método donde se ha intentado referenciar pero no se ha podido.

1
2
Exception in thread "main" java.lang.NullPointerException
    at Prog.main(Prog.java:5)
NullPointerException-1.out

Sin embargo, hay casos en los que la trazas de NullPointerException no es lo suficientemente precisa para determinar la causa de la excepción sin usar el debugger. En los siguientes ejemplos con elementos encadenados no es posible determinar cuál es la variable que ha originado la excepción por tener valor nulo.

1
2
3
4
a.b.c.i = 99;
a[i][j][k] = 99;
a.i = b.j;
x().y().i = 99;
NullPointerException-2.out

A partir de Java 14 las excepciones NullPointerException son más útiles e indican de forma precisa cual es el miembro de la línea de código que ha producido la excepción.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
Exception in thread "main" java.lang.NullPointerException:
       Cannot assign field "i" because "a" is null
   at Prog.main(Prog.java:5)

Exception in thread "main" java.lang.NullPointerException:
       Cannot read field "c" because "a.b" is null
   at Prog.main(Prog.java:5)

Exception in thread "main" java.lang.NullPointerException:
      Cannot load from object array because "a[i][j]" is null
   at Prog.main(Prog.java:5)

Exception in thread "main" java.lang.NullPointerException:
       Cannot read field "j" because "b" is null
   at Prog.main(Prog.java:5)
NullPointerException-3.out

Expresiones switch

La características de expresiones switch introducida en modo vista previa en las versiones de Java 12 y 13 se califica como estándar.

1
2
3
4
5
6
String numericString = switch(integer) { 
   case 0 -> "zero"; 
   case 1, 3, 5, 7, 9 -> "odd"; 
   case 2, 4, 6, 8, 10 -> "even"; 
   default -> "N/A"; 
};
Switch-1.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
String numericString = switch(integer) { 
   case 0 -> {
       String value = calculateZero();
       yield value;
   } ; 
   case 1, 3, 5, 7, 9 -> {
       String value = calculateOdd();
       yield value;
   };
   case 2, 4, 6, 8, 10 -> {
       String value = calculateEven();
       yield value;
   };
   default -> {
       String value = calculateDefault();
       yield value;
   };
};
Switch-2.java

ZGC para Windows y macOS

La versión del recolector de basura ZGC que permite pausas muy reducidas en memorias de unos pocos MB hasta varios TB ahora es posible utilizarla en los sistemas operativos macOS y Windows.

Nuevas características en vista previa

Bloques de texto

En esta nueva revisión de los bloques de texto se definen dos nuevos caracteres de escape. El terminador de línea para poder definir bloques de texto en varias líneas pero sin insertar saltos de línea en el bloque de texto y \s para evitar que los espacios en blanco sean eliminados por la operación trim.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
String text = """
               Lorem ipsum dolor sit amet, consectetur adipiscing \
               elit, sed do eiusmod tempor incididunt ut labore \
               et dolore magna aliqua.\
               """;

String colors = """
   red  \s
   green\s
   blue \s
   """;
TextBlocks.java

Records

Esta es la característica más destacada añadida al lenguaje que permite reducir significativamente el código necesario para algunas clases.

Los registros son clases que no contienen más datos que los públicos declarados. Evitan mucho del código que es necesario en Java para definir los constructores, los métodos getter, los setter e implementar de forma correcta los métodos equals y hashCode.

Para reducir el código de las clases de datos los registros adquieren automáticamente varios miembros:

  • Un campo privado y final para cada componente del estado en la descripción.
  • Un método de acceso de lectura para cada componente del estado de la descripción, con el mismo nombre y tipo.
  • Un constructor público cuya firma es la misma que el estado de la descripción que inicializa cada campo de su correspondiente argumento.
  • Una implementación de equals y hashCode de tal forma que dos registros son iguales si son del mismo tipo y contienen el mismo estado.
  • Una implementación de toString que incluye una representación de todos los componentes del registro con sus nombres.

Los registros tienen algunas restricciones:

  • No pueden extender ninguna otra clase y no pueden declarar campos que no sean los privados automáticos que corresponden a los componentes de la descripción del estado en la descripción. Cualquier otro campo debe ser declarado como static. estas restricciones aseguran que la descripción del estado define su representación.
  • Los registros son implícitamente final y no pueden ser abstract. Esto significa que no pueden ser mejorados por otra clase o registro.
  • Los componentes de un registro son implícitamente final. Esta restricción hace que sean inmutables.

Más allá de esta restricciones los registros se comportan como clases normales pudiendo declararse en su propio archivo de código fuente o anidada en otra clase, pueden ser genéricos, implementar interfaces e instanciarse con la palabra clave new. Pueden declarar métodos estáticos, propiedades estáticas, inicializadores estáticos, constructores, métodos de instancia y tipos anidados. El registro y los componentes individuales de los componentes pueden ser anotados.

Para dar soporte a los records al realizar tareas de reflection se añaden los siguientes métodos en la clase Class: RecordComponent[] getRecordComponents() y boolean isRecord()

De una clase como esta.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package ...;

public class PhoneNumber {

    private Integer lineNumber;
    private Integer prefix;
    private Integer areaCode;

    public Integer getLineNumber() {
        return lineNumber;
    }

    public Integer getPrefix() {
        return prefix;
    }

    public Integer getAreaCode() {
        return areaCode;
    }
 
    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null)
            return false;
        if (getClass() != o.getClass())
            return false;

        PhoneNumber that = (PhoneNumber) o;
        return super.equals(that)
            && Objects.equals(this.lineNumber, that.lineNumber)
            && Objects.equals(this.prefix, that.prefix)
            && Objects.equals(this.areaCode, that.areaCode);
    }
}
Records-1.java

Con los registros se define de la siguiente forma.

1
2
3
4
package ...;

public record PhoneNumber(Integer lineNumber, Integer prefix, Integer areaCode) {
}
Records-2.java

Pattern Matching para el operador instanceof

Al usar el operador instanceOf para comprobar si un objeto es una instancia de una clase si se realiza en un if posteriormente es necesario hacer un cast del objeto a la clase.

1
2
3
4
if (obj instanceof String) {
    String s = (String) obj;
    // use s
}
IfPatternMatching-1.java

Ahora el operador instanceof permite renombrar la variable y dentro de la rama usarla sin necesidad de realizar el cast, esto simplifica el código y evita posibles errores.

1
2
3
if (obj instanceof String s) {
    // use s
}
IfPatternMatching-2.java

En futuras proposiciones de mejoras para el lenguaje de programación Java está planificado soportar pattern matching para otras construcciones del lenguaje como expresiones switch y sentencias. La incorporación de pattern matching permitirá reducir la verbosidad del código haciéndolo más fácil de leer y modificar.

La posible implementación en Java quizá sea similar a la implementación de C# para pattern matching.


Comparte el artículo: