Referințe la metode (::) în Java

O referință la metodă (Clasa::metoda) este o lambda prescurtată care doar apelează o metodă existentă.

Sintaxă
Integer::parseInt        // metodă statică        s -> Integer.parseInt(s)
System.out::println      // metoda unui obiect    x -> System.out.println(x)
String::toUpperCase      // metodă pe parametru    s -> s.toUpperCase()
ArrayList::new           // constructor           () -> new ArrayList<>()

Ce face Referințe la metode (::)

Când o lambda nu face altceva decât să apeleze o metodă, o poți scrie mai scurt cu ::. lista.forEach(x -> System.out.println(x)) devine lista.forEach(System.out::println). Ca și lambda, referința are nevoie de o interfață funcțională ca tip țintă.

Există patru feluri: metodă statică (Integer::parseInt), metoda unui obiect anume (System.out::println, cos::adauga), metodă de instanță apelată pe primul parametru (String::length înseamnă s -> s.length()) și constructor (Elev::new, ArrayList::new, int[]::new).

Referințele sunt des folosite cu stream-uri și comparatori: map(String::strip), Comparator.comparing(Elev::nume). Dacă lambda face mai mult decât un singur apel, păstreaz-o ca lambda.

Exemple

Exemplul 1: Cele patru feluri de referințe

Fiecare referință înlocuiește lambda din comentariu. Elev::new alege constructorul cu un parametru String, potrivit cu Function<String, Elev>.

import java.util.List;
import java.util.function.Function;

record Elev(String nume) { }

public class Main {
    public static void main(String[] args) {
        List<String> texte = List.of("12", "7", "30");

        // 1. metodă statică: s -> Integer.parseInt(s)
        List<Integer> numere = texte.stream().map(Integer::parseInt).toList();
        System.out.println("numere: " + numere);

        // 2. metoda unui obiect anume: x -> System.out.println(x)
        numere.forEach(System.out::println);

        // 3. metodă de instanță pe parametru: s -> s.toUpperCase()
        List<String> orase = List.of("iași", "bacău").stream().map(String::toUpperCase).toList();
        System.out.println(orase);

        // 4. constructor: nume -> new Elev(nume)
        Function<String, Elev> creeaza = Elev::new;
        System.out.println(creeaza.apply("Teodora"));
    }
}

Rezultat

numere: [12, 7, 30]
12
7
30
[IAȘI, BACĂU]
Elev[nume=Teodora]

Exemplul 2: Referințe în stream-uri și comparatori

Comparator.comparing(Produs::pret) sortează după preț, map(Produs::nume) extrage numele, iar String[]::new creează array-ul de mărimea potrivită.

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

record Produs(String nume, double pret) {
    boolean laReducere() {
        return pret < 10;
    }
}

public class Main {
    public static void main(String[] args) {
        List<Produs> raft = List.of(
            new Produs("cafea", 39.9), new Produs("apă", 3.5),
            new Produs("ciocolată", 8.75), new Produs("ceai", 14.0));

        List<Produs> ieftine = raft.stream().filter(Produs::laReducere).toList();
        System.out.println("la reducere: " + ieftine);

        String[] nume = raft.stream()
            .sorted(Comparator.comparing(Produs::pret))
            .map(Produs::nume)
            .toArray(String[]::new);
        System.out.println("după preț: " + Arrays.toString(nume));
    }
}

Rezultat

la reducere: [Produs[nume=apă, pret=3.5], Produs[nume=ciocolată, pret=8.75]]
după preț: [apă, ciocolată, ceai, cafea]

Greșeli frecvente

  • Pui paranteze: System.out::println() nu compilează. Referința numește metoda, nu o apelează.
  • Folosești o referință ambiguă, de exemplu Integer::toString cu Function<Integer, String>: există și metoda statică toString(int), și cea de instanță, iar compilatorul spune incompatible types: invalid method reference. Scrie lambda explicit.
  • Forțezi o referință acolo unde lambda face mai multe lucruri. x -> x.strip().toLowerCase() nu are o referință echivalentă; lasă lambda.

Comenzi înrudite

  • Expresii lambda: O lambda este o funcție scurtă, fără nume, scrisă ca parametri -> corp, folosită oriunde se așteaptă o interfață funcțională.
  • Stream-uri (ideea): Un stream descrie o prelucrare de date ca un lanț: sursă, operații intermediare (filter, map, sorted) și o operație finală (toList, count, collect).
  • Comparatori (Comparable și Comparator): Comparable dă ordinea naturală a unei clase (compareTo); Comparator descrie orice altă ordine și se combină cu comparing, thenComparing și reversed.
  • Constructori: Constructorul are numele clasei, nu are tip returnat și pregătește obiectul nou; o clasă poate avea mai mulți constructori.