1. Giriş
Method reference (rus dilində — metod istinadı) Java-da mövcud metodu (və ya konstruktoru) funksional interfeysin həyata keçirilməsi kimi ötürməyə imkan verən xüsusi sintaksisdir. Əgər imzalar üst-üstə düşürsə, metodu lambda ifadəsi gözlənilən yerə “ötürmək” olar.
Sintaksis:
Sinif::metod
obyekt::metod
Sinif::new
Əgər lambda “yolda” yazılan kiçik funksiyadırsa, method reference sadəcə “artıq mövcud olan metodu ötür” deməkdir. Bu, resepti yenidən yazmaq əvəzinə reseptin səhifəsinə keçid verməyə bənzəyir.
Sadə nümunə
Bunun əvəzinə:
list.forEach(s -> System.out.println(s));
Belə yaza bilərsiniz:
list.forEach(System.out::println);
Qısa və oxunaqlıdır, elə deyilmi?
2. Method reference növləri
Metod istinadlarının dörd əsas forması var. Bax bunlar — və daha artığı lazım deyil.
Statik metoda istinad
Sintaksis: Sinif::statikMetod
Nümunə:
Function<Integer, String> intToString = String::valueOf;
System.out.println(intToString.apply(123)); // "123"
Eyni şey lambda ilə:
Function<Integer, String> intToString = i -> String.valueOf(i);
Obyektin qeyri-statik metoduna istinad
Sintaksis: obyekt::metod
Nümunə:
PrintStream printer = System.out;
Consumer<String> consumer = printer::println;
consumer.accept("Salam, dünya!");
Lambda ilə ekvivalentdir:
Consumer<String> consumer = s -> printer.println(s);
Sinifin qeyri-statik metoduna istinad
Sintaksis: Sinif::metod
Burada funksional interfeysin birinci parametri metodun çağırılacağı obyektə çevrilir.
Nümunə:
Function<String, Integer> stringLength = String::length;
System.out.println(stringLength.apply("Java")); // 4
Burada String::length bu funksiyaya çevrilir: (String s) -> s.length()
Konstruktor istinadı
Sintaksis: Sinif::new
Nümunə:
Supplier<ArrayList<String>> listSupplier = ArrayList::new;
ArrayList<String> list = listSupplier.get();
Lambda ilə ekvivalentdir:
Supplier<ArrayList<String>> listSupplier = () -> new ArrayList<>();
3. Method reference nə vaxt istifadə olunmalıdır?
Lambda sadəcə mövcud metodu əlavə məntiq olmadan çağırırsa, method reference istifadə etmək rahatdır — kod daha qısa və oxunaqlı olur.
Nümunə: siyahının sıralanması
Bunun əvəzinə:
List<String> names = Arrays.asList("İvan", "Petr", "Anna");
names.sort((a, b) -> a.compareToIgnoreCase(b));
Belə yaza bilərsiniz:
names.sort(String::compareToIgnoreCase);
Nümunə: kolleksiyaların emalı
Bunun əvəzinə:
list.forEach(s -> System.out.println(s));
Ümumiyyətlə çox qısa:
list.forEach(System.out::println);
Nümunə: elementlərin çevrilməsi
Bunun əvəzinə:
List<String> numbers = Arrays.asList("1", "2", "3");
List<Integer> ints = numbers.stream()
.map(s -> Integer.parseInt(s))
.collect(Collectors.toList());
Belə mümkündür:
List<Integer> ints = numbers.stream()
.map(Integer::parseInt)
.collect(Collectors.toList());
Stream API və util sinfi Collectors barədə daha ətraflı 30-cu səviyyədə öyrənəcəksiniz :P
4. Method reference və lambda ifadələrinin müqayisəsi
Ekvivalentlik
Method reference və lambda ifadəsi çox vaxt bir-birini əvəz edə bilər. İmzalar üst-üstə düşdükdə hər ikisi funksional interfeysi həyata keçirir.
Nümunə:
Consumer<String> c1 = s -> System.out.println(s);
Consumer<String> c2 = System.out::println;
Method reference nə vaxt daha yaxşıdır?
- Lambda mövcud metodu əlavə məntiq olmadan sadəcə çağırdıqda.
- Xüsusən uzun çağırış zəncirlərində oxunaqlılığı artırmaq üçün.
- Açıqca göstərmək istəyəndə: “burada sadəcə metod çağırışı var”.
Method reference nə zaman uyğun deyil?
- Əlavə məntiq (doğrulama, şərtlər, səhv emalı) lazım olduqda.
- Parametrləri metod çağırışından əvvəl çevirmək/transformasiya etmək gərəkəndə.
Nümunə:
list.forEach(s -> {
if (s != null) System.out.println(s);
});
// Burada method reference uyğun deyil, yalnız lambda.
5. Təcrübə: lambda ifadələrini method reference ilə yenidən yazmaq
Nümunə 1: Heyvan adlarının çıxarılması
List<String> animals = Arrays.asList("Pişik", "İt", "Tutuquşu");
animals.forEach(animal -> System.out.println(animal));
Belə olur:
animals.forEach(System.out::println);
Nümunə 2: Sətirləri ədədlərə çevirmək
List<String> numbers = Arrays.asList("10", "20", "30");
List<Integer> ints = numbers.stream()
.map(s -> Integer.parseInt(s))
.collect(Collectors.toList());
Belə olur:
List<Integer> ints = numbers.stream()
.map(Integer::parseInt)
.collect(Collectors.toList());
Nümunə 3: Obyektlərin ada görə sıralanması
List<Animal> animalList = ...;
animalList.sort((a, b) -> a.getName().compareTo(b.getName()));
Belə olur:
animalList.sort(Comparator.comparing(Animal::getName));
Burada Animal::getName — sinifin qeyri‑statik metoduna istinaddır.
Nümunə 4: Konstruktor vasitəsilə obyektlərin yaradılması
Supplier<Dog> dogFactory = () -> new Dog();
Dog dog = dogFactory.get();
Belə olur:
Supplier<Dog> dogFactory = Dog::new;
Dog dog = dogFactory.get();
6. İmza uyğunlaşdırılması necə işləyir
Method reference yalnız o halda istifadə oluna bilər ki, metodun imzası funksional interfeysin abstrakt metodu ilə üst-üstə düşsün.
@FunctionalInterface
interface IntToString {
String convert(int value);
}
public class Demo {
public static String intToHex(int value) {
return Integer.toHexString(value);
}
public static void main(String[] args) {
IntToString converter = Demo::intToHex;
System.out.println(converter.convert(255)); // ff
}
}
Burada Demo::intToHex uyğundur, çünki int qəbul edir və String qaytarır.
7. Method reference və parametrləri olan konstruktorlar
Konstruktor parametrlər qəbul edirsə belə, imzalar uyğun gəldiyi müddətdə method reference istifadə oluna bilər.
@FunctionalInterface
interface AnimalFactory {
Animal create(String name);
}
class Animal {
private String name;
public Animal(String name) { this.name = name; }
public String getName() { return name; }
}
AnimalFactory factory = Animal::new;
Animal cat = factory.create("Barsik");
System.out.println(cat.getName()); // Barsik
8. Method reference istifadə edərkən tipik səhvlər
Xəta №1: İmzaların uyğunsuzluğu.
Əgər metodun imzası interfeysin abstrakt metodu ilə üst-üstə düşmürsə, kompilyator xəta verəcək. Məsələn, interfeys iki parametr gözləyir, amma istinad bir parametrli metoda yönəlir.
Xəta №2: Sinifin qeyri-statik metoduna obyekt olmadan istinad.
Sinif::metod formasını istifadə edəndə interfeysin birinci parametri çağırışın obyekti olur. Parametrlərin sayını və ya sırasını səhv etsəniz — uyğunlaşdırma xətası alacaqsınız.
Xəta №3: Əlavə məntiq tələb olunan yerdə method reference istifadə etmək.
Şərt, jurnal qeydiyyatı və ya istisna emalı tələb olunursa — metod istinadı deyil, lambda istifadə edin.
Xəta №4: Aşırı yüklənmiş metodlara istinad.
Əgər sinifdə eyni adlı bir neçə metod varsa, kompilyator hansını seçəcəyini anlaya bilməyə bilər. Bəzən funksional interfeysin tipini açıq göstərmək kömək edir.
Xəta №5: Qeyri-statik metoda obyekt olmadan istinad.
Məsələn, String::toUpperCase map-də düzgün işləyir, çünki birinci parametr — elə String obyektinin özüdür. Amma uyğun kontekstdən kənarda, harada ki statik metod gözlənilir, bu xəta ilə nəticələnəcək.
GO TO FULL VERSION