Java 8 es la próxima versión del lenguaje, y contendrá muchas mejoras. Su característica más conocida son los lambdas, que se pueden usar para instancias interfaces funcionales (interfaces con 1 solo método) con una sintaxis concisa. Sin embargo, Java 8 también incluye muchas otras novedades interesantes, que no tienen tanta prensa. ¡Veamos juntos que hay en Java 8 más allá de los lambdas!

Interfaces funcionales

Quizás el agregado más importante de Java 8 sean los lambdas. Muy relacionado a los lambdas se encuentra el concepto de "interfaces funcionales". Una interfaz es funcional si declara exactamente 1 método abstracto. Por ejemplo, java.lang.Runnable es una interfaz funcional porque define un único método abstracto.

Los métodos predeterminados (marcados con "default") no son abstractos, por lo cual una interfaz funcional puede definir varios métodos predeterminados.

Se agrega una nueva anotación: @FunctionalInterface. Esta anotación puede usarse para declarar la intención que la interfaz es funcional. Esta anotación la utiliza el compilador, y sirve para evitar error y agregar métodos a interfaces que se desean sean funcional. Es como la anotación @Override, que denota intención.

Lambdas

Una característica única de las interfaces funcionales es que pueden ser instanciadas usando "lambdas". Veamos algunos ejemplos de lambdas.

Una lista de entradas separada por comas, indicando el tipo de datos, y un bloque que retorna la suma de ambos parámetros:

(int x, int y) -> { return x + y; }

Una lista de entradas separada por comas, infiriendo el tipo de datos, y retorna la suma de ambos:

(x, y) -> x + y

Un único parámetro con el tipo de dato inferido, y retorna ese parámetro al cuadrado:

x -> x * x

Sin valores de entrada, retorna un valor:

() -> x

Un único parámetro de entrada con el tipo de datos inferido, y un bloque que retorna void a la derecha:

x -> { System.out.println(x); }

También hay algunas formas "cortas" de escribir lambdas comunes:

String::valueOf             x -> String.valueOf(x)
Object::toString            x -> x.toString()
x::toString                 () -> x.toString()
ArrayList::new              () -> new ArrayList<>()

Por supuesto, en Java existe la sobrecarga de métodos. Los operadores lambda intentan usar el método que mejor se adapte a la expresión: se tienen en cuenta los valores de entrada, de salida y las excepciones declaradas.

Por ejemplo:

Comparator c = (a, b) -> Integer.compare(a.length(), b.length());

El método "compare" de Comparator tiene dos parámetros de entrada , y retorna un int. Esto es consistente con el lambda de la derecha, por lo cual es una asignación válida.

Runnable r = () -> { System.out.println("Running!"); }

El método "run" de la interfaz Runnable no recibe parámetros de entrada, y retorna void. Esto es consistente con el lambda de la derecha, y resulta en una asginación válida.

También se tienen en cuenta las excepciones chequeadas declaradas en la firma del método. El lambda sólo puede lanzar excepciones chequeadas si están declaradas en la interfaz.

Ejemplos de uso de lambdas en Java 8

Actualmente, para agregarle un listener a un botón podemos hacer:
 
btn.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
        System.out.println("Hola, mundo!");
    }
});     
 
Con expresiones lambda de Java 8 podemos hacer exactametne lo mismo con la siguiente expresión:
 
btn.setOnAction(
    event -> System.out.println("Hola, mundo!")
); 
 
También se puede usar el operador :: para hacer referencia a métodos estáticos de una clase:
 
public class Utils {
    public static int compareByLength(String in, String out){
        return in.length() - out.length();
    }
}
 
public class MyClass {
    public void doSomething() {
        String[] args = new String[] {"microsoft","apple","linux","oracle"}
        Arrays.sort(args, Utils::compareByLength);
    } 
}     
 
... o también podemos acceder a métodos no estáticos:
 
public class MyClass implements Comparable<MyObject> {
 
    @Override
    public int compareTo(MyObject obj) {return ...}
 
    public void doSomething() {
        MyObject myObject = new MyObject();
        Arrays.sort(args, myObject::compareTo);
    } 
}
 

La página con la documentación de expresiones lambda tiene mucha más información.

Métodos predeterminados en las interfaces

Las interfaces ahora podrán definir métodos estáticos. En las bibliotecas de Java, es bastante común que si existe una interfaz Foo, exista también una clase utilitaria Foos que contiene métodos estáticos para realizar tareas con instancias de Foo (por ejemplo, la interfaz Collection y la clase Collections). Con esta nueva característica, todos estos métodos pueden ubicarse directamente en la interfaz.

Otro cambio más importante es que ahora se pueden definir implementaciones predeterminadas para los métodos, usando la palabra clave default. Por ejemplo, se agregó el método forEach a la interfaz java.lang.Iterable:

 
public default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}
 

Hasta ahora, no se podía agregar métodos a una interfaz (sin que impacte en quienes implementaba dicha interfaz, ya que debían implementar los nuevos métodos). A partir de ahora, los proveedores de la interfaz la pueden extender y usar implementaciones predeterminadas, lo cual permite evolucionar una interfaz y a la vez mantener la compatibilidad con versiones anteriores. En el JDK 8 hay muchas interfaces clásicas que fueron extendidas con métodos predeterminado

Nuevo API de fechas

Hace rato que se mencionaba la necesidad de un nuevo API de fechas/horas/calendarios que tenga una mejor interfaz que el (viejo y odiado) java.util.Date. Para esto llega el nuevo paquete java.timeque contendrá al nuevo API. Les va a resultar facil de usar, especialmente para los que conozcan JodaTime.

Prácticamente todo lo que expone el API es inmutable, para evitar las confusiones que presente el API actual.

La integración con el API actual de fechas es muy simple a partir de nuevos métodos:

Las nuevas clases más importantes:

  • Instantes, básicamente, un timestamp numérico. Es una clase útil para realizar logs y usar para frameworks de persistencia.
  • LocalDatesirve para almacenar una fecha sin hora. Por ejemplo, un cumpleaños como "16-12-1979".
  • LocalTimesirve para almacenar una hora sin fecha. Por ejemplo, un horario de apertura a las "9:30".
  • LocalDateTimesirve para almacenar una fecha con hora. Por ejmplo, puede almacenar "1979-12-16T12:30".
  • ZonedDateTimealmacena hora y fecha con información de uso horario.

Algunos ejemplos de código:

 
//imprimir la fecha actual
LocalDate date = LocalDate.now();
System.out.printf("%s-%s-%s",date.getYear(), date.getMonthValue(), date.getDayOfMonth());
 
//vamos a sumar 5 horas
LocalTime time = LocalTime.now();
LocalTime newTime;
newTime = time.plus(5, HOURS);
 
// o también
newTime = time.plusHours(5);
 
// o también
Period p = Period.of(5, HOURS);
newTime = time.plus(p);
 

La página de la documentación de java.time y este artículo introductorio a java.time tienen más información al respecto.

java.util.stream

Este nuevo API provee utilidades para para permitir operaciones "estilo programación funcional" sobre flujo de valores. La forma más simple de obtener un Stream seguramente sea a partir de una colección:

 
Stream<T> stream = collection.stream();
 

Los stream son parecidos a los iteradores. Los valores van "fluyendo" (piensen en el agua que fluye en una canilla abierta), y se van. Los stream sólo pueden recorrerse 1 vez. Los stream también pueden ser infinitos.

Más interesante es que los stream pueden ser secuenciales o paralelos, característica que puede cambiarse usando los métodos stream.sequential() o stream.parallel(). Las acciones en un stream secuencial ocurren de forma secuencial en un único thread. En cambio, las acciones sobre un stream paralelo pueden ocurrir todas a la vez en múltiples threads.

Un ejemplo más concreto de uso de streams:

 
int sumOfWeights = blocks.stream().filter(b -> b.getColor() == RED)
                                  .mapToInt(b -> b.getWeight())
                                  .sum();
 

Los stream tienen un API fluído que permite encadenar invocaciones. Hay dos tipos de invocaciones: "intermedias" y "finales". Las invocaciones intermedias permiten continuar con el flujo de las operaciones, mientras que las operaciones terminales "consumen" al stream y se tienen que invocar para finalizar la operacion. sum() es una operación terminal.

En general, el uso de streams involucra:

  1. Obtener un stream.
  2. Realizar una o más operaciones intermedias.
  3. Realizar una operación final.

Las operaciones intermedias no se ejecutan de forma inmediata, sino que son lazy. La operación final es quien desencadena el procesamiento de los elementos del stream.

Pueden leer más en la documentación sobre los streams.

Repetición de anotaciones

Este es uno de los cambios "menores" de Java 8, que sin embargo resulta muy práctico para algunas situaciones. Consiste en que será posible anotar un objeto muchas veces con la misma anotación.

Un ejemplo del tutorial:

 
@Alert(role = "Manager")
@Alert(role = "Administrator")
public class UnauthorizedAccessException { ... }
 

Para que esto funcione, la anotación @Alert tiene que estar anotada con java.lang.annotation.Repeatable.

Es posible que deba revisarse el código de algunas herramientas que dependían en el procesamiento de anotaciones. Se agregaron nuevos métodos al JDK como AnnotatedElement.getAnnotationsByType(Class)y AnnotatedElement.getDeclaredAnnotationsByType(Class) que permiten descubrir las anotaciones repetidas de un elemento.

Pueden leer más en la documentación de anotaciones repetibles.

Eliminación del espacio PermGen

Este es uno de los cambios grandes: el espacio de memoria PermGen (Permanent Generation) se elimina completamente, y se reemplaza por un espacio nuevo llamado Metaspace.

La consecuencia obvia es que se ignorarán los parámetros de la JVM "PermSize" y "MaxPermSize", y por otro lado dejaremos de ver el java.lang.OutOfMemoryError: PermGen error.

Obviamente, esto no significa que no tendremos que preocuparnos por el uso de memoria. El espacio Metaspace cambiará de tamaño dinámicamente dependiendo de la demanda de la aplicación en tiempo de ejecución. Aparecen nuevos parámetros para la JVM que nos permitirán limitar el tamaño del Metaspace, si así lo deseamos.

Les recomiendo leer el artículo From PermGen to Metaspace para entender los detalles de este cambio.

Nuevo método para ordenar arrays en paralelo

El método Arrays.parallelSort es nuevo, y utiliza el framework Fork/Join de Java 7 para asignar las tareas de ordenamiento a varios threads disponibles en el pool de threads.

Para pocos elementos seguirá siendo conveniente el algoritmo tradicional de ordenamiento, pero se puede lograr una diferencia muy importante en rendimiento cuando tratamos con Arrays de miles de elementos. Les recomiendo leer Arrays.sort versus Arrays.parallelSort para ver una comparativa de rendimiento de ambos métodos.

Nashorn, el nuevo engine JavaScript

Java 8 incluirá un nuevo engine JavaScript (denominado "Nashorn"), que tiene mucho mejor rendimiento que el actual Rhino. La presentación Project Nashorn in Java 8 contiene diapositivas que explican las características de este nuevo engine, y ejemplos de código de integración con Java.

Leer más

    • Javi

      Se están cargando Java.
      Están mezclando un montón de cosas que no son orientadas a objetos y esta saliendo una mezcla difícil de digerir.

    • Martin

      Muchas de estas mejoras ya existen hace mucho tiempo en .net Lambda existe desde el framework 3.5. Oracle esta llevando a la ruina a Java!

    • xavi

      el tema de las expresiones lambda que explicaste aquí, me aclaro bastante algunas dudas que tenia....gracias y ojala sigas aportando siempre tu valiosa ayuda.

    • xavi

      Cual es la traducción de Target Type? y a que se refiere?...gracias

      mi buzon de correo: xavigon_20@hotmail.com

    • Norberto

      Eso de acceder a los métodos estáticos usando :: ya existía en Visual C++ 6.0, dios mio qué están haciendo con java...Oracle la estás KGDO

    • ORACLE , solo quiere su dinero,... y reducir costos de mano de obra de programación, son los nuevos tiempos de las anotaciones y las expresiones Lambda

    • Oscar

      Y tanto que defendi a java de C# y lo hacen cada vez mas parecido

    • ariel

      muy interesante, pero la verdad no me gusta mucho funciones lambda eso es nativo de c++ y c++ es un lenguaje demasiado artesanal para mi gusto, a pesar de que dichas funciones aportan cierta riqueza a tu codigo me parece incomodo usarlas,

    • ariel

      por cierto a tooooooda la gente que atribuye funciones lambda a c# o visual basic estan muy equivocados es propio de c++ y c# no es mas que una super copia de java y c++(tomaron lo mas poderoso e interesante de ambos lenguajes y lo juntaron en uno solo), si no me creen alguien se pregunto porque c++ soporta polimorfismo y c# no??, aunque no faltara aquel que diga que si, pero no!!!, no lo soporta SIMPLEMENTE LO SIMULA(pero eso lo copio de java con el uso de interfaces)

    • Pedrito

      guau guau guau nap nap toc tic tuc guau gua gua gua guau guau!

    • No veo mucha utilidad de las expresiones lambda más allá de reducir la "fontanería" declarativa cuando el cuerpo de lo que tienes que hacer es ya reducido a cambio de legibilidad (y esto es muy subjetivo), pero cuando tienes un cuerpo run() o de un método de un listener con suficiente entidad (la mayor parte de los casos), la pretendida legibilidad se invierte.

      El tema de las implementaciones por defecto lo considero muy útil. En general, estoy de acuerdo con los comentarios en que no parece acertado "alejar" Java de la OOP. Precísamente C++ es un (potente, eso si) enjendro por ser una mezcla de todo...

      Buen artículo.

    • Es curioso leer que esta versión de Java no sirve para nada, cuando es todo lo contrario. Esta versión supone un primer paso hacia la programación funcional, hacia la escalabilidad y hace el multicore.

    • Pepito

      que pinchilezco...

    • Para algunos, hace tiempo que muchos esperábamos lambdas (Mataría que soporten full closures para olvidarnos de los if else de una manera elegante).
      Me parece que Java 8 va por buen camino, hace muchísimo tiempo que Smalltalk tiene closures(Bloques) y me parece que hace al código muy declarativo. Creo que es la idea, revelar la intención de una manera sencilla. Puede ser que tengamos diferentes puntos de vista para expresarnos, o que la forma de expresión del API no sea la mejor, pero creo que los lambdas son un cambio positivo para Java.

    • enric

      A mi me gusta Java 8 pues lleva el lenguaje Java a un nivel mas funcional, es decir a tratar de escribir el código como lo hacemos cuando hablamos. Aquí se ilustra un ejemplo, explicando como implementar la funcionalidad "ordenar una lista de personas por apellido" http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html

    • Muy bueno lo que hacen la verdad, los que dicen que se están cagando a Java lo mas seguro es que aún siguen con el horrible framework JavaSwing. Yo utilizo Scala y otros dinámicos pero la verdad es que esta tardado en la fiesta.

    • Me parece que lo que hace Oracle es agregar a Java herramientas para seguir siendo uno de los lenguajes preferidos, si con lambdas puedo crear la misma aplicación con ménos código pues bienvenida sea.
      SoftMAS.

    • Y despues dicen que .net es una torta. Las expresiones lambda hacen parte de .net desde hace años.

    • idecasso

      Las lambdas son muy útiles, simplemente vean lenguajes como Groovy que soportan clausuras, similares a las lambdas, en unas lineas de código puedes recorrer todo el contenido de un directorio y procesarlo, en lugar de muchas lineas que se vuelven complicadas de manejar. Al final de cuentas nadie obliga a usar las nuevas características del lenguaje, siempre es posible usar la forma anterior de trabajo.

    • Opino como se dice al comienzo del artículo que no tiene sentido centrar Java 8 en las lambda expresiones, es un artificio para escribir el código más compacto, pero sin más importancia. El tipo stream es mucho más interesante
      http://laprogramacionnoesunarte.blogspot.com.es/p/apuntes-de-java-8.html

    Cargar Más

    Deja tus comentarios

    Post comment as a guest

    0

    Seguinos en Facebook.

    Publicá tus artículos.

    Publicar Convertite en redactor para Dos Ideas y compartí tus conocimientos a una comunidad que sigue creciendo!
    Quiero publicar

    Los Comentarios.

    invitado
    hasta ahora no sabia que era el cinismo pero ahora que lo se me he dado cuenta porque he tenido tant...
    Dai
    Es broma?
    busquen el significado de cinismo.
    esta el antiguo significado y el moderno,
    el moderno...
    Yan
    Hola:
    Unas duda, Drools ¿tiene una interfaz gráfica para poder generar y editar reglas? o todo se t...
    Maxi
    Gracias por la info, esta bien explicado y funciono como solución a mi problema que tenia con el mét...
    jonybuzz
    Cierto. Y más desafiante: Qué pasa si dejamos ir algo que sí funciona? Algo que sentimos que puede m...

    Inspiración.

    "Si tú tienes una manzana y yo tengo una manzana e intercambiamos las manzanas, entonces tanto tú como yo seguiremos teniendo una manzana cada uno. Pero si tú tienes una idea y yo tengo una idea, e intercambiamos las ideas, entonces ambos tendremos dos ideas"

    Bernard Shaw