Riferimenti ai metodi in Java

1. Panoramica

Uno dei cambiamenti più graditi in Java 8 è stata l'introduzione delle espressioni lambda, poiché queste ci consentono di rinunciare a classi anonime, riducendo notevolmente il codice boilerplate e migliorando la leggibilità.

I riferimenti ai metodi sono un tipo speciale di espressioni lambda . Sono spesso usati per creare semplici espressioni lambda facendo riferimento a metodi esistenti.

Esistono quattro tipi di riferimenti al metodo:

  • Metodi statici
  • Metodi di istanza di oggetti particolari
  • Metodi di istanza di un oggetto arbitrario di un tipo particolare
  • Costruttore

In questo tutorial, esploreremo i riferimenti ai metodi in Java.

2. Riferimento a un metodo statico

Inizieremo con un esempio molto semplice, capitalizzando e stampando un elenco di stringhe :

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

Possiamo ottenere ciò sfruttando una semplice espressione lambda chiamando direttamente il metodo StringUtils.capitalize () :

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

Oppure, possiamo usare un riferimento al metodo per fare semplicemente riferimento al metodo statico maiuscolo :

messages.forEach(StringUtils::capitalize);

Si noti che i riferimenti ai metodi utilizzano sempre l' operatore :: .

3. Riferimento a un metodo di istanza di un oggetto particolare

Per dimostrare questo tipo di riferimento al metodo, consideriamo due classi:

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 creiamo un oggetto BicycleComparator per confrontare le dimensioni del telaio della bicicletta:

BicycleComparator bikeFrameSizeComparator = new BicycleComparator();

Potremmo usare un'espressione lambda per ordinare le biciclette in base alle dimensioni del telaio, ma avremmo bisogno di specificare due biciclette per il confronto:

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

Invece, possiamo usare un riferimento al metodo per far passare il parametro handle del compilatore:

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

Il riferimento al metodo è molto più pulito e più leggibile, poiché la nostra intenzione è chiaramente mostrata dal codice.

4. Riferimento a un metodo di istanza di un oggetto arbitrario di un tipo particolare

Questo tipo di riferimento al metodo è simile all'esempio precedente, ma senza dover creare un oggetto personalizzato per eseguire il confronto.

Creiamo un elenco di numeri interi che vogliamo ordinare:

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

Se usiamo un'espressione lambda classica, entrambi i parametri devono essere passati esplicitamente, mentre l'utilizzo di un riferimento al metodo è molto più semplice:

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

Anche se è ancora una riga, il riferimento al metodo è molto più facile da leggere e capire.

5. Riferimento a un costruttore

Possiamo fare riferimento a un costruttore nello stesso modo in cui abbiamo fatto riferimento a un metodo statico nel nostro primo esempio. L'unica differenza è che useremo la nuova parola chiave.

Creiamo un array Bicycle da un elenco di stringhe con marchi diversi:

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

Innanzitutto, aggiungeremo un nuovo costruttore alla nostra classe Bicycle :

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

Successivamente, useremo il nostro nuovo costruttore da un riferimento al metodo e creeremo un array Bicycle dall'elenco String originale :

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

Si noti come abbiamo chiamato i costruttori Bicycle e Array utilizzando un metodo di riferimento, dando al nostro codice un aspetto molto più conciso e chiaro.

6. Ulteriori esempi e limitazioni

Come abbiamo visto finora, i riferimenti ai metodi sono un ottimo modo per rendere il nostro codice e le nostre intenzioni molto chiari e leggibili. Tuttavia, non possiamo usarli per sostituire tutti i tipi di espressioni lambda poiché hanno alcune limitazioni.

La loro principale limitazione è il risultato di quello che è anche il loro più grande punto di forza: l'output dell'espressione precedente deve corrispondere ai parametri di input della firma del metodo di riferimento .

Vediamo un esempio di questa limitazione:

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

Questo semplice caso non può essere espresso con un riferimento al metodo, perché il metodo printf richiede 3 parametri nel nostro caso e l'utilizzo di createBicyclesList (). ForEach () consentirebbe al riferimento al metodo di dedurre solo un parametro (l' oggetto Bicycle ).

Infine, esploriamo come creare una funzione senza operazioni a cui è possibile fare riferimento da un'espressione lambda.

In questo caso, vorremo utilizzare un'espressione lambda senza utilizzare i suoi parametri.

Per prima cosa, creiamo il metodo doNothingAtAll :

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

Poiché si tratta di un metodo varargs, funzionerà in qualsiasi espressione lambda, indipendentemente dall'oggetto di riferimento o dal numero di parametri dedotti.

Vediamolo ora in azione:

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

7. Conclusione

In questo rapido tutorial, abbiamo appreso quali sono i riferimenti ai metodi in Java e come utilizzarli per sostituire le espressioni lambda, migliorando così la leggibilità e chiarendo l'intento del programmatore.

Tutto il codice presentato in questo articolo è disponibile su GitHub.