Java: Pasar un método como parámetro

Introducción

¿Eres un programador apasionado por Java? Si es así, estás a punto de descubrir una técnica poderosa que revolucionará tu forma de programar. En este artículo, aprenderás a utilizar interfaces funcionales para pasar tus métodos como parámetros en Java. Además, te sumergirás en el fascinante mundo del patrón Callback.

El paquete java.util.function y las interfaces funcionales

Nuestro primer paso será identificar el tipo de retorno de la función que queremos pasar.

Esto nos sirve entre otras cosas para entender de que manera se va a usar la función.

ClaseTipo de retorno original del métodoParámetro original del métodoUsoMétodo de la interfaz funcional
Consumervoidsubtipo de ObjectHacer algo con datosapply
Predicatebooleansubtipo de ObjectValidacióntest
Suppliersubtipo de Objectsin parámetrosGenerar datosget
Functionsubtipo de Objectsubtipo de ObjectConvertir datosapply
Runnablevoidsin parámetrosEjecutar una acciónrun
Tabla 1: Interfaz Funcional y Uso

A partir de Java 8 tenemos las interfaces funcionales que básicamente son interfaces con un método único, lo que nos permite en sintaxis ahorrarnos mucho código, ya que la implementación es trivial (o talacha como diríamos los mexicanos), y como es trivial podemos en su lugar usar un lambda.

Ejemplos de como pasar un método como parámetro

Función sin parámetros que no regresa nada

Supongamos que tenemos una función como esta

public void funcionCerrar() {
System.exit(0);
}

Este es el caso ideal para un Runnable, si queremos usar este método podemos llamarlo desde la misma clase como

private void ponerEscucha(JButton boton, Runnable miMetodo) {
 boton.addActionListener(e -> miMetodo.run());
}

Función que regresa un booleano

Supongamos que tenemos un método con una decisión como un switch

public boolean isTechnicalFighter(String name) {
    return switch (name) {
        case "El Santo" -> true;
        case "El cavernario" -> false;
        case "El bulldog" -> false;
        default -> false;
    };
}

En este caso lo ideal es usar un Predicate

public boolean isTechnical(String nombreLuchador, Predicate<String> predicate) {
    return predicate.test(nombreLuchador);
}

Función que mapea

Por ejemplo si tenemos una función para ayudarnos a crear objetos como al siguiente

public JButton creaBotón(String texto) {
    return new JButton(texto);
}

Lo ideal en este caso es usar una función

private void creacion(String textoBotón, Function<String, JButton> creaBoton) {
    creaBoton.apply(textoBotón);
}

Recuerda que el primer parámetro del genérico será el tipo de dato de entrada y el segundo el tipo de dato de salida

Función que genera datos

Para este caso en particular el ejemplo más básico sería obtener un valor aleatoriamente

public int numeroDeLASuerte() {
 return (int) (Math.random() * 100);
}

Y el tipo para este caso sería un Supplier

private void imprimeNumeroDeLaSuerte(Supplier<Integer> numeroDeLASuerte) {
    System.out.println(numeroDeLASuerte.get());
}

Si quieres ver más ejemplos, junto con la explicación de que es un lambda revisa mi artículo sobre generadores.

¿Y cómo llamamos al método que usaremos para pasar un método como parámetro?

Sencillo, mediante una referencia estática.

Como principio si nuestro método tiene como modificador la palabra static, usaremos el nombre de la clase seguido del operador :: y luego pondremos el nombre de la función

this.ponerEscuchar(this.jButton1, CerrarBotonListener::funcionCerrar);

En caso de que no tenga static, usaremos la palabra reservada this para referenciar la instancia actual de la clase seguido del operador :: y luego pondremos el nombre de la función

this.ponerEscuchar(this.jButton1, this::funcionCerrar);

al usar esta notación no necesitaremos especificar si es un Consumer, Predicate, Supplier, Runnable o Function.

Si por alguna razón deseamos tener como variable la función sí deberemos declarar el tipo de interfaz funcional que es, por ejemplo, si es un Consumer

Consumer<String> greetPrinter= greeting-> System.out.println(greeting);

o si es un supplier

Supplier<String[]> luchadorSupplier = () -> {
 return new String[]{"El Santo", "El Cavernario", "Blue Demon", "Bull Dog"};
};

Si bien especificar el tipo de datos de los parámetros usando genéricos es opcional, es una buena práctica ya que nos asegura que nuestros métodos van a funcionar de manera correcta y sin sorpresas, para Runnable nunca se especifica.

Interfaces funcionales más legibles

Si bien la Tabla 1 es útil, en algunos casos obtendremos código que a pesar de ser corto es un poco confuso. Para evitar eso hay especializaciones de algunas de las clases y las veremos a continuación.

  1. Datos Primitivos Simplificados: Para una manipulación eficiente de datos primitivos, descubre las subclases de Supplier, como IntSupplier, que hacen que la provisión de valores sea más eficiente y efectiva.
  2. Conversión de Tipos Eficiente: La interfaz BiFunction es tu aliada para realizar conversiones de tipos complejas con múltiples parámetros, lo que te permite mantener un código limpio y legible.
  3. Operaciones de Identidad: Cuando tu función requiere que el tipo de entrada sea igual al tipo de salida, simplifica tus operaciones utilizando UnaryOperator para un código más conciso.
  4. Operaciones Binarias Avanzadas: Considera el uso de BinaryOperator si necesitas realizar operaciones que devuelvan el mismo tipo tanto en entrada como en salida. Esta interfaz es especialmente útil para cálculos de máximos y mínimos de manera eficiente.
  5. Manejo de Dos Elementos: Para situaciones en las que necesitas trabajar con dos elementos sin esperar ningún resultado, BiConsumer es la elección adecuada.

¿Y en el mundo real como se usa?

Su principal uso es cuando deseas agregar el Stream API a tu código existente.

Despedida

¡Enhorabuena! Ahora estás equipado con una potente técnica de programación en Java que te ayudará a optimizar tus proyectos mientras haces muy poco código. Si deseas explorar más sobre este fascinante tema o tienes alguna pregunta, no dudes en explorar este sitio para obtener más recursos y tutoriales. ¡A tirar código al fallo!

javatlacati
http://javapro.org

javapro.org