Referencias de m茅todos en Java 8

    Introducci贸n

    El mas dulce az煤car sint谩ctica agregado a Java hasta ahora son definitivamente Lambda Expressions.

    Java es un lenguaje detallado y eso puede obstaculizar la productividad y la legibilidad. Reducir el c贸digo repetitivo y repetitivo siempre ha sido una tarea popular entre los desarrolladores de Java, y generalmente se busca un c贸digo limpio, legible y conciso.

    Lambda Expressions elimin贸 la necesidad de escribir c贸digo repetitivo engorroso cuando se trata de algunas tareas comunes al permitir a los desarrolladores llamarlas sin que pertenezcan a una clase y pasarlas como si fueran objetos.

    Estas expresiones han tenido un uso importante con la API de Java Streams y Spring’s WebFlux marco para la creaci贸n de aplicaciones din谩micas reactivas.

    Otra caracter铆stica realmente 煤til agregada a Java 8 son las referencias a m茅todos, que hacen que las expresiones Lambda sean mucho m谩s concisas y simples, al invocar (hacer referencia) a los m茅todos usando un nombre de m茅todo cuando la expresi贸n Lambda se hubiera usado simplemente para llamar a un m茅todo.

    Referencias de m茅todos

    Las referencias a m茅todos son esencialmente expresiones Lambda abreviadas, que se utilizan para invocar m茅todos.

    Constan de dos partes:

    Class::method;
    

    Y un ejemplo com煤n ser铆a imprimir los resultados de, por ejemplo, suscribirse a un servicio de publicaci贸n o Java Stream:

    someCodeChain.subscribe(System.out::println);
    

    Repasemos un ejemplo de c贸digo imperativo, que luego pasaremos a c贸digo funcional a trav茅s de Expresiones Lambda y finalmente lo acortaremos mediante Referencias de m茅todo.

    Haremos una clase simple:

    public class Employee {
        private int id;
        private String name;
        private int wage;
        private String position;
    
        // Constructor, getters and setters
    
        @Override
        public String toString() {
            return "Name: " + name + ", Wage: " + wage + ", Position: " + position;
        }
    
        public int compareTo(Employee employee) {
            if (this.wage <= employee.wage) {
                return 1;
            } else {
                return -1;
            }
        }
    }
    

    Si formamos esta clase en una colecci贸n, como un ArrayList, no pudimos clasificarlo con el m茅todo de utilidad .sort() ya que no implementa el Comparable interfaz.

    Lo que podemos hacer es definir un new Comparator para estos objetos mientras llama al .sort() m茅todo:

    Employee emp1 = new Employee(1, "David", 1200, "Developer");
    Employee emp2 = new Employee(2, "Tim", 1500, "Developer");
    Employee emp3 = new Employee(3, "Martha", 1300, "Developer");
    
    ArrayList<Employee> employeeList = new ArrayList<>();
    employeeList.add(emp1);
    employeeList.add(emp2);
    employeeList.add(emp3);
    
    Collections.sort(employeeList, new Comparator<Employee>() {
        public int compare(Employee emp1, Employee emp2) {
            return emp1.compareTo(emp2);
        }
    });
    
    System.out.println(employeeList);
    

    Ejecutar este c贸digo producir谩:

    [Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]
    

    Aqu铆, la clase an贸nima (Comparator) est谩 definiendo los criterios de comparaci贸n. Podemos hacerlo mucho m谩s simple y m谩s corto usando una expresi贸n Lambda:

    Collections.sort(employeeList, (e1, e2) -> e1.compareTo(e2));
    

    Ejecutar este fragmento de c贸digo producir谩:

    [Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]
    

    Por otra parte, dado que todo lo que estamos haciendo con esta expresi贸n Lambda es llamar a un solo m茅todo, podemos hacer referencia solo a ese m茅todo:

    Collections.sort(employeeList, Employee::compareTo);
    

    Y esto tambi茅n producir谩:

    [Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]
    

    Tipos de referencia de m茅todo

    Las referencias de m茅todos se pueden utilizar en un par de escenarios diferentes:

    • M茅todos est谩ticos: Class::staticMethodName
    • M茅todos de instancia de objetos particulares: object::instanceMethodName
    • M茅todos de instancia de objetos de arbitraje: Class::methodName
    • Referencia del constructor: Class::new

    Repasemos todos estos tipos a trav茅s de algunos ejemplos simples.

    Referencias de m茅todos est谩ticos

    Puede hacer referencia a cualquier static m茅todo de una clase simplemente llamando a su clase contenedora con el nombre del m茅todo.

    Definamos una clase con un static y luego referenciarlo desde otra clase:

    public class ClassA {
        public static void raiseToThePowerOfTwo(double num) {
            double result = Math.pow(num, 2);
            System.out.println(result);
        }
    }
    

    Y ahora, de otra clase, usemos el static m茅todo de utilidad:

    public class ClassB {
        public static void main(String[] args) {
            List<Double> integerList = new ArrayList<>();
            integerList.add(new Double(5));
            integerList.add(new Double(2));
            integerList.add(new Double(6));
            integerList.add(new Double(1));
            integerList.add(new Double(8));
            integerList.add(new Double(9));
    
            integerList.forEach(ClassA::raiseToThePowerOfTwo);
        }
    }
    

    Ejecutar este fragmento de c贸digo producir谩:

    25.0
    4.0
    36.0
    1.0
    64.0
    81.0
    

    Hay muchas clases de Java que ofrecen static m茅todos de utilidad que se pueden utilizar aqu铆. En nuestro ejemplo hemos utilizado un m茅todo personalizado, aunque no muy 煤til en este caso.

    M茅todos de instancia de objetos particulares

    Puede llamar a un m茅todo desde un objeto instanciado particular haciendo referencia al m茅todo utilizando la variable de referencia del objeto.

    Esto se ilustra con mayor frecuencia mediante un comparador personalizado. Usaremos lo mismo Employee class de antes y la misma lista para resaltar la diferencia entre estos dos:

    public class Employee {
        private int id;
        private String name;
        private int wage;
        private String position;
    
        // Constructor, getters and setters
    
        @Override
        public String toString() {
            return "Name: " + name + ", Wage: " + wage + ", Position: " + position;
        }
    
        public int compareTo(Employee employee) {
            if (this.wage <= employee.wage) {
                return 1;
            } else {
                return -1;
            }
        }
    }
    

    Ahora, definamos un CustomComparator:

    public class CustomComparator {
        public int compareEntities(Employee emp1, Employee emp2) {
            return emp1.compareTo(emp2);
        }
    }
    

    Y finalmente, completemos una lista y ord茅nela:

    Employee emp1 = new Employee(1, "David", 1200, "Developer");
    Employee emp2 = new Employee(2, "Tim", 1500, "Developer");
    Employee emp3 = new Employee(3, "Martha", 1300, "Developer");
    
    ArrayList<Employee> employeeList = new ArrayList<>();
    employeeList.add(emp1);
    employeeList.add(emp2);
    employeeList.add(emp3);
    
    // Initializing our CustomComparator
    CustomComparator customComparator = new CustomComparator();
    
    // Instead of making a call to an arbitrary Employee
    // we're now providing an instance and its method
    Collections.sort(employeeList, customComparator::compareEntities);
    
    System.out.println(employeeList);
    

    Ejecutar este c贸digo tambi茅n producir谩:

    [Name: Tim, Wage: 1500, Position: Developer, Name: Martha, Wage: 1300, Position: Developer, Name: David, Wage: 1200, Position: Developer]
    

    La principal diferencia es que al agregar otra capa, a trav茅s del CustomComparator, podemos agregar m谩s funcionalidad para comparar y quitarla de la clase en s铆. Una clase como Employee no debe cargarse con una l贸gica de comparaci贸n compleja y esto da como resultado un c贸digo m谩s limpio y legible.

    Por otro lado, a veces no deseamos definir comparadores personalizados e introducir uno es simplemente una molestia. En tales casos, llamar铆amos a un m茅todo desde un objeto arbitrario de un tipo particular, que se muestra en la siguiente secci贸n.

    M茅todos de instancia de objetos arbitrarios

    Este ejemplo ya se mostr贸 al comienzo del art铆culo cuando redujimos el enfoque imperativo a un enfoque funcional a trav茅s de Lambda Expressions.

    Aunque, en buena medida, dado que este enfoque se usa con mucha frecuencia, echemos un vistazo a otro ejemplo:

    List<Integer> integerList = new ArrayList<>();
    integerList.add(new Integer(5));
    integerList.add(new Integer(2));
    integerList.add(new Integer(6));
    integerList.add(new Integer(1));
    integerList.add(new Integer(8));
    integerList.add(new Integer(9));
    
    // Referencing the non-static compareTo method from the Integer class
    Collections.sort(integerList, Integer::compareTo);
    
    // Referencing static method
    integerList.forEach(System.out::print);
    

    Ejecutar este fragmento de c贸digo producir铆a:

    125689
    

    Si bien esto puede parecer lo mismo que una llamada a un m茅todo est谩tico, no lo es. Esto es equivalente a llamar a la expresi贸n Lambda:

    Collections.sort(integerList, (Integer a, Integer b) -> a.compareTo(b));
    

    Aqu铆, la distinci贸n es m谩s obvia. Si tuvi茅ramos que llamar a un static m茅todo, se ver铆a as铆:

    Collections.sort(integerList, (Integer a, Integer b) -> SomeClass.compare(a, b));
    

    Constructores de referencia

    Puede hacer referencia al constructor de una clase de la misma manera que har铆a referencia a un static m茅todo.

    Podr铆a usar una referencia a un constructor en lugar de la instanciaci贸n de clase cl谩sica:

    // Classic instantiation
    Employee employee = new Employee();
    
    // Constructor reference
    Employee employee2 = Employe::new;
    

    Seg煤n el contexto, si hay varios constructores presentes, se utilizar谩 el adecuado si se hace referencia a 茅l:

    Stream<Employee> stream = names.stream().map(Employee::new);
    

    Debido a una serie de nombres, si un Employee(String name) constructor est谩 presente, se utilizar谩.

    Otra forma en que podr铆a usar referencias de constructor es cuando quiera mapear una secuencia en una matriz, manteniendo el tipo particular. Si simplemente lo mapea y luego llama toArray(), obtendr铆as una variedad de Objects en lugar de tu tipo particular.

    Si lo intentamos, di:

    Employee[] employeeArray = employeeList.toArray();
    

    Por supuesto, recibir铆amos un error del compilador ya que .toArray() devuelve una matriz de Objects. Lanzarlo tampoco ayudar谩:

    Employee[] employeeArray = (Employee[]) employeeList.toArray();
    

    Pero esta vez, ser谩 una excepci贸n de tiempo de ejecuci贸n: ClassCastException.

    Podemos evitar eso con:

    // Making a list of employees
    List<String> employeeList = Arrays.asList("David", "Scott");
    
    // Mapping a list to Employee objects and returning them as an array
    Employee[] employeeArray = employeeList.stream().map(Employee::new).toArray(Employee[]::new);
    
    // Iterating through the array and printing information
    for (int i = 0; i < employeeArray.length; i++) {
        System.out.println(employeeArray[i].toString());
    }
    

    Y con eso, obtenemos el resultado:

    Name: David, Wage: 0, Position: null
    Name: Scott, Wage: 0, Position: null
    

    Conclusi贸n

    Las referencias a m茅todos son un tipo de expresiones Lambda que se utilizan para hacer referencia simplemente a un m茅todo en su llamada. Con ellos, escribir c贸digo puede ser mucho m谩s conciso y legible.

    Lambda Expressions ha presentado a los desarrolladores de Java un enfoque m谩s funcional en la programaci贸n que les permite evitar escribir c贸digo detallado para operaciones simples.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *