Tutorial de Java 8
"Java sigue vivo y la gente está empezando a entenderlo".
Bienvenido a mi introducción a Java 8. Este artículo lo llevará paso a paso a través de todas las características nuevas de Java 7 a Java 8. Con ejemplos de código rápidos y simples, podemos aprender a usar interfaces predeterminadas, referencias de
métodos y Anotaciones repetibles . Al final del artículo nos familiarizaremos con la Stream API.
Sin charlas innecesarias: ¡solo código y comentarios! ¡Adelante!
Métodos predeterminados para interfaces
Java 8 nos permite agregar métodos no abstractos (que están implementados) a las interfaces agregando el archivo
default
. Esta característica también se conoce como
Métodos de extensión . A continuación se muestra el primer ejemplo:
interface Formula {
double calculate(int a);
default double sqrt(int a) {
return Math.sqrt(a);
}
}
Además del método abstracto
calculate
, la interfaz
Formula
también define un método predeterminado
sqrt
. Las clases que implementan esta interfaz solo necesitan implementar
calculate
. El método predeterminado
sqrt
se puede utilizar de forma inmediata.
Formula formula = new Formula() {
@Override
public double calculate(int a) {
return sqrt(a * 100);
}
};
formula.calculate(100);
formula.sqrt(16);
La interfaz
Formula
se implementa como una clase anónima. El código es redundante: 6 líneas para implementación
sqrt(a * 100)
. Como veremos en la siguiente sección, existe una forma más bonita de implementar un único método en Java 8.
expresiones lambda
Comencemos con un ejemplo simple de cómo ordenar una lista de cadenas en versiones anteriores de Java:
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
El método estático
Collections.sort
acepta una lista y un comparador en el orden en que se ordenará la lista. Siempre puedes crear una clase de comparación anónima y transmitirla. En lugar de crear una clase anónima, en Java 8 puedes escribir una forma más corta, una expresión lambda.
Collections.sort(names, (String a, String b) -> {
return b.compareTo(a);
});
Como puede ver, el código es mucho más corto y más fácil de leer. Pero esto se puede acortar aún más:
Collections.sort(names, (String a, String b) -> b.compareTo(a));
Para un cuerpo con una línea, puedes omitir
{}
la palabra
return
. Pero puedes hacerlo aún más corto:
Collections.sort(names, (a, b) -> b.compareTo(a));
El compilador de Java conoce los tipos de argumentos, por lo que también puedes omitirlos. Profundicemos en las expresiones lambda y comprendamos cómo se pueden utilizar.
Interfaces funcionales
¿Cómo encajan las expresiones lambda en el sistema de tipos de Java? Cada lambda corresponde a un tipo descrito en la interfaz. Por lo tanto, una interfaz funcional debe contener sólo un método abstracto. Cada expresión lambda de este tipo corresponderá a este método abstracto. Dado que los métodos predeterminados no son abstractos, puede crear métodos predeterminados en interfaces funcionales según sea necesario. También podemos usar interfaces arbitrarias como expresiones lambda si solo hay un método abstracto en esta interfaz. Para cumplir con estos requisitos es necesario agregar
@FucntionalInterface
una anotación. El compilador lo sabe y generará una excepción si desea proporcionar más de un método abstracto. Ejemplo:
@FunctionalInterface
interface Converter<F, T> {
T convert(F from);
}
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted);
Tenga en cuenta que el código también se compilará si
@FunctionalInterface
se omite la anotación.
Referencias de métodos y constructores
El ejemplo anterior también se puede reducir aún más utilizando referencias de métodos:
Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted);
Java 8 le permite pasar referencias a un método o constructor agregando
::
. El ejemplo anterior muestra cómo podemos hacer referencia a un método estático, aunque también podemos hacer referencia a métodos no estáticos:
class Something {
String startsWith(String s) {
return String.valueOf(s.charAt(0));
}
}
Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted);
Veamos cómo
::
funciona con los constructores. Para empezar, definiremos una clase de ejemplo con diferentes constructores:
class Person {
String firstName;
String lastName;
Person() {}
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
A continuación, definiremos una interfaz para crear nuevos objetos:
interface PersonFactory<P extends Person> {
P create(String firstName, String lastName);
}
En lugar de implementar una fábrica de creación, lo uniremos todo con
::
la ayuda del constructor:
PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");
Creamos un enlace al constructor a través de
Person::new
. El compilador de Java seleccionará automáticamente el constructor correcto que coincida con la firma del método
PersonFactory.create
. ... Continuará. Desafortunadamente, no encontré una manera de guardar un borrador del artículo, y esto es realmente extraño, y el tiempo de traducción se acabó, así que lo terminaré más tarde. Para todos los que saben y entienden inglés -
Artículo original . Si tiene sugerencias para corregir la traducción, escriba de cualquier forma disponible para usted.
Mi cuenta de Github
GO TO FULL VERSION