Referencias de métodos en Java

1. Información general

Uno de los cambios más bienvenidos en Java 8 fue la introducción de expresiones lambda, ya que nos permiten prescindir de clases anónimas, lo que reduce en gran medida el código repetitivo y mejora la legibilidad.

Las referencias a métodos son un tipo especial de expresiones lambda . A menudo se usan para crear expresiones lambda simples haciendo referencia a métodos existentes.

Hay cuatro tipos de referencias de métodos:

  • Métodos estáticos
  • Métodos de instancia de objetos particulares
  • Métodos de instancia de un objeto arbitrario de un tipo particular
  • Constructor

En este tutorial, exploraremos las referencias a métodos en Java.

2. Referencia a un método estático

Comenzaremos con un ejemplo muy simple, capitalizando e imprimiendo una lista de cadenas :

List messages = Arrays.asList("hello", "baeldung", "readers!");

Podemos lograr esto aprovechando una simple expresión lambda llamando directamente al método StringUtils.capitalize () :

messages.forEach(word -> StringUtils.capitalize(word));

O podemos usar una referencia de método para referirnos simplemente al método estático de capitalización :

messages.forEach(StringUtils::capitalize);

Observe que las referencias a métodos siempre utilizan el operador :: .

3. Referencia a un método de instancia de un objeto particular

Para demostrar este tipo de referencia de método, consideremos dos clases:

public class Bicycle { private String brand; private Integer frameSize; // standard constructor, getters and setters } public class BicycleComparator implements Comparator { @Override public int compare(Bicycle a, Bicycle b) { return a.getFrameSize().compareTo(b.getFrameSize()); } }

Y creemos un objeto BicycleComparator para comparar los tamaños de cuadros de bicicletas:

BicycleComparator bikeFrameSizeComparator = new BicycleComparator();

Podríamos usar una expresión lambda para ordenar las bicicletas por tamaño de cuadro, pero necesitaríamos especificar dos bicicletas para comparar:

createBicyclesList().stream() .sorted((a, b) -> bikeFrameSizeComparator.compare(a, b));

En su lugar, podemos usar una referencia de método para que el compilador maneje el parámetro pasando por nosotros:

createBicyclesList().stream() .sorted(bikeFrameSizeComparator::compare);

La referencia del método es mucho más limpia y legible, ya que nuestra intención se muestra claramente en el código.

4. Referencia a un método de instancia de un objeto arbitrario de un tipo particular

Este tipo de referencia de método es similar al ejemplo anterior, pero sin tener que crear un objeto personalizado para realizar la comparación.

Creemos una lista de enteros que queremos ordenar:

List numbers = Arrays.asList(5, 3, 50, 24, 40, 2, 9, 18);

Si usamos una expresión lambda clásica, ambos parámetros deben pasarse explícitamente, mientras que usar una referencia de método es mucho más sencillo:

numbers.stream() .sorted((a, b) -> a.compareTo(b)); numbers.stream() .sorted(Integer::compareTo);

Aunque sigue siendo un resumen, la referencia del método es mucho más fácil de leer y comprender.

5. Referencia a un constructor

Podemos hacer referencia a un constructor de la misma manera que hicimos referencia a un método estático en nuestro primer ejemplo. La única diferencia es que usaremos la nueva palabra clave.

Creemos una matriz de bicicletas a partir de una lista de cadenas con diferentes marcas:

List bikeBrands = Arrays.asList("Giant", "Scott", "Trek", "GT");

Primero, agregaremos un nuevo constructor a nuestra clase Bicycle :

public Bicycle(String brand) { this.brand = brand; this.frameSize = 0; } 

A continuación, usaremos nuestro nuevo constructor a partir de una referencia de método y crearemos una matriz Bicycle a partir de la lista String original :

bikeBrands.stream() .map(Bicycle::new) .toArray(Bicycle[]::new); 

Observe cómo llamamos a los constructores Bicycle y Array usando una referencia de método, lo que le da a nuestro código una apariencia mucho más concisa y clara.

6. Ejemplos y limitaciones adicionales

Como hemos visto hasta ahora, las referencias a métodos son una excelente manera de hacer que nuestro código y nuestras intenciones sean muy claros y legibles. Sin embargo, no podemos usarlos para reemplazar todo tipo de expresiones lambda ya que tienen algunas limitaciones.

Su principal limitación es el resultado de lo que también es su mayor fortaleza: la salida de la expresión anterior debe coincidir con los parámetros de entrada de la firma del método referenciado .

Veamos un ejemplo de esta limitación:

createBicyclesList().forEach(b -> System.out.printf( "Bike brand is '%s' and frame size is '%d'%n", b.getBrand(), b.getFrameSize()));

Este caso simple no se puede expresar con una referencia de método, porque el método printf requiere 3 parámetros en nuestro caso, y el uso de createBicyclesList (). ForEach () solo permitiría que la referencia del método infiera un parámetro (el objeto Bicycle ).

Finalmente, exploremos cómo crear una función sin operación a la que se pueda hacer referencia desde una expresión lambda.

En este caso, querremos usar una expresión lambda sin usar sus parámetros.

Primero, creemos el método doNothingAtAll :

private static  void doNothingAtAll(Object... o) { }

Como es un método varargs, funcionará en cualquier expresión lambda, sin importar el objeto referenciado o la cantidad de parámetros inferidos.

Ahora veámoslo en acción:

createBicyclesList() .forEach((o) -> MethodReferenceExamples.doNothingAtAll(o)); 

7. Conclusión

En este tutorial rápido, aprendimos qué referencias a métodos hay en Java y cómo usarlas para reemplazar expresiones lambda, mejorando así la legibilidad y aclarando la intención del programador.

Todo el código presentado en este artículo está disponible en GitHub.