Referências de método em Java

Referências de método em Java

1. Visão geral

Uma das mudanças mais bem-vindas no Java 8 foi a introdução de https://www..com/java-8-lambda-expressions-tips [expressões lambda], pois elas permitem o abandono de classes anônimas, reduzindo bastante o código padrão e melhorar a legibilidade.

*As referências de método são um tipo especial de expressões lambda* . Eles geralmente são usados ​​para criar expressões lambda simples fazendo referência a métodos existentes.

Existem quatro tipos de referências de método:

  • Métodos estáticos

  • Métodos de instância de objetos específicos

  • Métodos de instância de um objeto arbitrário de um tipo específico *Construtor

Neste tutorial, exploraremos as referências de método em Java.

2. Referência a um método estático

Começaremos com um exemplo muito simples, capitalizando e imprimindo uma lista de Strings:

List<String> messages = Arrays.asList("hello", "", "readers!");

Podemos conseguir isso utilizando uma expressão lambda simples chamada https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html#capitalize-java.lang.String- Método [_StringUtils.capitalize () _] diretamente:

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

Ou, podemos usar uma referência de método para simplesmente nos referir ao método estático capitalize:

messages.forEach(StringUtils::capitalize);
Observe que as referências de método sempre utilizam o operador _

_.

3. Referência a um método de instância de um objeto específico

Para demonstrar esse tipo de referência de método, vamos considerar duas classes:

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());
    }

}

E, vamos criar um objeto BicycleComparator para comparar os tamanhos de quadros de bicicletas:

BicycleComparator bikeFrameSizeComparator = new BicycleComparator();

Poderíamos usar uma expressão lambda para classificar bicicletas por tamanho de quadro, mas precisaríamos especificar duas bicicletas para comparação:

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

Em vez disso, podemos usar uma referência de método para que o parâmetro do manipulador do compilador nos passe:

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

A referência do método é muito mais limpa e mais legível, pois nossa intenção é claramente mostrada pelo código.

4. Referência a um método de instância de um objeto arbitrário de um tipo particular

Esse tipo de referência de método é semelhante ao exemplo anterior, mas sem a necessidade de criar um objeto personalizado para realizar a comparação.

Vamos criar uma lista Integer que queremos classificar:

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

Se usarmos uma expressão lambda clássica, os dois parâmetros precisarão ser explicitamente transmitidos, enquanto o uso de uma referência de método é muito mais direto:

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

Mesmo sendo uma linha única, a referência do método é muito mais fácil de ler e entender.

5. Referência a um construtor

Podemos fazer referência a um construtor da mesma maneira que referenciamos um método estático em nosso primeiro exemplo. A única diferença é que usaremos a palavra-chave new.

Vamos criar uma matriz Bicycle a partir de uma lista String com marcas diferentes:

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

Primeiro, adicionaremos um novo construtor à nossa classe Bicycle:

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

Em seguida, usaremos nosso novo construtor a partir de uma referência de método e criaremos uma matriz Bicycle a partir da lista String original:

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

Observe como chamamos os construtores Bicycle e Array usando uma referência de método, dando ao nosso código uma aparência muito mais concisa e clara.

6. Exemplos e limitações adicionais

Como vimos até agora, as referências de métodos são uma ótima maneira de tornar nosso código e intenções muito claros e legíveis. No entanto, não podemos usá-los para substituir todos os tipos de expressões lambda, pois elas têm algumas limitações.

A principal limitação é o resultado da maior força:* a saída da expressão anterior precisa corresponder aos parâmetros de entrada da assinatura do método referenciado *.

Vamos ver um exemplo dessa limitação:

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

Este caso simples não pode ser expresso com uma referência de método, porque o método printf requer 3 parâmetros em nosso caso, e o uso de createBicyclesList (). ForEach () _ só permitiria que a referência de método inferisse um parâmetro (o objeto _Bicycle).

Por fim, vamos explorar como criar uma função sem operação que pode ser referenciada a partir de uma expressão lambda.

Nesse caso, queremos usar uma expressão lambda sem usar seus parâmetros.

Primeiro, vamos criar o método doNothingAtAll:

private static <T> void doNothingAtAll(Object... o) {
}

Como é um método https://www..com/java-varargs [varargs], ele funcionará em qualquer expressão lambda, independentemente do objeto de referência ou do número de parâmetros inferidos.

Agora, vamos vê-lo em ação:

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

7. Conclusão

Neste tutorial rápido, aprendemos quais referências de método existem em Java e como usá-las para substituir expressões lambda, melhorando a legibilidade e esclarecendo a intenção do programador.

Todo o código apresentado neste artigo está disponível over no GitHub.