1. Məlumat əvəzinə davranışın ötürülməsi
Ümumiyyətlə funksiyanı parametr kimi ötürmək niyə lazımdır?
Təsəvvür edin: sizdə siyahını çeşidləyən bir metod var. Bəs o, necə çeşidləməyi haradan bilsin? Əlifbaya görə? Uzunluğa görə? Doğum tarixinə görə? Hər hal üçün ayrıca metod yazmaq olardı, amma bu, tezliklə dəhşətli kopipestə çevriləcək.
Bunun əvəzinə Java sizə davranışı (yəni funksiyanı və ya lambdanı) ötürməyə imkan verir; o, necə çeşidləməyi, filtrləməyi, çevirməyi və s. müəyyən edir. Bu, kodu çevik və yenidən istifadə oluna bilən edir.
Nümunə: Comparator ilə çeşidləmə
List<String> names = List.of("Anna", "Boris", "Vika");
// Davranış ötürürük: elementləri necə müqayisə etməli (sətir uzunluğuna görə)
names.stream()
.sorted((a, b) -> a.length() - b.length())
.forEach(System.out::println);
Nümunə: Predicate ilə filtrasiya
List<String> names = List.of("Anna", "Boris", "Vika");
// Davranış ötürürük: kimi saxlamalı (adın uzunluğu 4 simvoldan böyükdür)
names.stream()
.filter(name -> name.length() > 4)
.forEach(System.out::println);
Hər iki halda siz məntiqi metoda sərt şəkildə “tikmədiniz”, əksinə ona hər bir elementə tətbiq ediləcək kiçik bir davranış verdiniz.
Faydası: daha az təkrarlama, daha çox çeviklik
Oxşar kodlu, lakin fərqli məntiqi olan onlarla metod əvəzinə, funksiyanı “nə etməli” kimi qəbul edən bir universal metod yazırsınız. Bu, vaxtınıza qənaət edir, səhvləri azaldır və kodu test etməyi asanlaşdırır.
2. Funksiyaların ötürülməsi sintaksisi
Lambda-ifadələr arqument kimi
Ən çox yayılmış üsul — elə metodun çağırıldığı yerdəcə ox -> ilə lambdanı yazmaqdır:
list.forEach(item -> System.out.println(item));
Hətta daha qısa ola bilər, əgər uyğun metod artıq varsa — metod istinadı (::):
list.forEach(System.out::println);
Metod istinadları (method references)
Əgər sizdə artıq uyğun metod varsa, ona :: kimi istinad ötürmək olar:
// Adi metod
public static boolean isLongName(String name) {
return name.length() > 4;
}
// Metoda istinadı ötürmək
names.stream()
.filter(MyClass::isLongName)
.forEach(System.out::println);
Bu, metodun imzası funksional interfeysdə gözlənilənlə (Predicate<T>, Function<T, R>, Comparator<T> və s.) uyğun gəlirsə işləyir.
3. Standart kitabxanadan nümunələr
Collections.sort və Comparator
List<String> names = new ArrayList<>(List.of("Anna", "Boris", "Vika"));
// Adın uzunluğuna görə çeşidləmə
names.sort((a, b) -> a.length() - b.length());
System.out.println(names); // [Vika, Anna, Boris]
Stream.filter və Predicate
List<String> names = List.of("Anna", "Boris", "Vika");
// Yalnız 'V' ilə başlayan adları saxlayın
names.stream()
.filter(name -> name.startsWith("V"))
.forEach(System.out::println); // Vika
Stream.map və Function
List<String> names = List.of("Anna", "Boris", "Vika");
// Adları böyük hərflərə çevirmək
names.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
Optional.ifPresent və Consumer
Optional<String> opt = Optional.of("Salam!");
// Əgər dəyər varsa, onu çap et
opt.ifPresent(s -> System.out.println("Sətirdə: " + s));
4. Təcrübə: funksiya-parametrləri ilə öz metodlarımızı yazırıq
Bilgiləri praktikada tətbiq etməyin vaxtıdır! Gəlin, funksiyanı parametr kimi qəbul edən və onu daxilində istifadə edən bir neçə metod yazaq.
Nümunə 1: Siyahı elementlərini işləyən metod (forEach öz üslubumuzla)
Tutaq ki, bizdə istifadəçilər siyahısı var (User). Hər bir istifadəçi üzərində hansısa əməliyyatı yerinə yetirmək istəyirik — məsələn, adı çıxışa vermək, e-mail göndərmək, bonus yazmaq və s. Həmin əməliyyatı sərt şəkildə “tikmək” əvəzinə, onu parametr kimi — Consumer<User> — ötürək!
import java.util.List;
import java.util.function.Consumer;
class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
}
public class UserProcessor {
// Consumer<User> qəbul edən metod
public static void processUsers(List<User> users, Consumer<User> action) {
for (User user : users) {
action.accept(user);
}
}
public static void main(String[] args) {
List<User> users = List.of(
new User("Anna", 25),
new User("Boris", 30),
new User("Vika", 22)
);
// Bütün istifadəçilərin adlarını çıxışa ver
processUsers(users, user -> System.out.println("Ad: " + user.name));
// Bonusu yaz (nümunə üçün sadəcə çıxışa veririk)
processUsers(users, user -> System.out.println(user.name + " bonus aldı!"));
}
}
Nümunə 2: Elementlərin filtrasiyası üçün metod (Predicate)
Şərtə uyğun gələn istifadəçiləri (məsələn, yalnız yetkinləri) qaytaran metod yazaq:
import java.util.List;
import java.util.ArrayList;
import java.util.function.Predicate;
public class UserFilter {
public static List<User> filterUsers(List<User> users, Predicate<User> condition) {
List<User> result = new ArrayList<>();
for (User user : users) {
if (condition.test(user)) {
result.add(user);
}
}
return result;
}
public static void main(String[] args) {
List<User> users = List.of(
new User("Anna", 25),
new User("Boris", 17),
new User("Vika", 22)
);
// Yalnız yetkinləri filtr edirik
List<User> adults = filterUsers(users, user -> user.age >= 18);
adults.forEach(user -> System.out.println(user.name)); // Anna, Vika
}
}
Nümunə 3: Çevirici metod (Function)
İndi isə istifadəçilər siyahısını onların adları siyahısına (və ya istənilən başqa çevirməyə) çevirən metod:
import java.util.List;
import java.util.ArrayList;
import java.util.function.Function;
public class UserMapper {
public static <R> List<R> mapUsers(List<User> users, Function<User, R> mapper) {
List<R> result = new ArrayList<>();
for (User user : users) {
result.add(mapper.apply(user));
}
return result;
}
public static void main(String[] args) {
List<User> users = List.of(
new User("Anna", 25),
new User("Boris", 17),
new User("Vika", 22)
);
// Adların siyahısını alırıq
List<String> names = mapUsers(users, user -> user.name);
System.out.println(names); // [Anna, Boris, Vika]
// Yaşların siyahısını alırıq
List<Integer> ages = mapUsers(users, user -> user.age);
System.out.println(ages); // [25, 17, 22]
}
}
Nümunə 4: Generator metod (Supplier)
Bəzən “tələb üzrə” hansısa dəyəri almaq lazımdır — məsələn, təsadüfi ədəd generasiya etmək, obyekt yaratmaq, cari vaxtı əldə etmək. Bunun üçün Supplier<T> interfeysi uyğundur.
import java.util.function.Supplier;
public class ValueGenerator {
public static int getValue(Supplier<Integer> supplier) {
return supplier.get();
}
public static void main(String[] args) {
// Təsadüfi ədəd alırıq
int random = getValue(() -> (int)(Math.random() * 100));
System.out.println("Təsadüfi ədəd: " + random);
// Cari vaxtı millisaniyələrlə alırıq
long time = getValue(System::currentTimeMillis);
System.out.println("Vaxt: " + time);
}
}
5. Vahid tətbiq: nümunələri birləşdiririk
Tutaq ki, biz sadə “İstifadəçilər siyahısı” tətbiqi hazırlayırıq. Artıq bacarırıq:
- İstifadəçiləri şərtə görə filtr etmək (məsələn, yalnız yetkinlər);
- İstifadəçiləri nəyəsə çevirmək (ad, e-mail, yaş);
- Hər istifadəçi üzərində hərəkətlər icra etmək (çap, bonus yazmaq);
- Tələb üzrə dəyərlər generasiya etmək (məsələn, yeni istifadəçilər yaratmaq).
İndi bu metodlardan istifadə etməklə, kodu hər dəfə yeni tapşırığa görə yenidən yazmadan çevik verilənlər emalı ssenariləri qura bilərik.
import java.util.*;
import java.util.function.*;
public class UserApp {
static class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return name + " (" + age + ")";
}
}
// Universal metod: istifadəçiləri emal etmək
static void processUsers(List<User> users, Consumer<User> action) {
for (User user : users) action.accept(user);
}
// Universal filtr
static List<User> filterUsers(List<User> users, Predicate<User> condition) {
List<User> result = new ArrayList<>();
for (User user : users) if (condition.test(user)) result.add(user);
return result;
}
// Universal çevirici
static <R> List<R> mapUsers(List<User> users, Function<User, R> mapper) {
List<R> result = new ArrayList<>();
for (User user : users) result.add(mapper.apply(user));
return result;
}
public static void main(String[] args) {
List<User> users = List.of(
new User("Anna", 25),
new User("Boris", 17),
new User("Vika", 22)
);
// 1. Bütün istifadəçiləri çıxışa ver
processUsers(users, user -> System.out.println("İstifadəçi: " + user));
// 2. Yalnız yetkinləri tap
List<User> adults = filterUsers(users, user -> user.age >= 18);
System.out.println("Yetkinlər: " + adults);
// 3. Yetkinlərin adlarını al
List<String> adultNames = mapUsers(adults, user -> user.name);
System.out.println("Yetkinlərin adları: " + adultNames);
// 4. Yetkinlərə bonus yaz
processUsers(adults, user -> System.out.println(user.name + " bonus aldı!"));
}
}
Bu niyə “adi” yanaşmadan daha yaxşıdır?
Bu üsul çeviklik verir: eyni metodları tamamilə fərqli ssenarilərdə, sadəcə başqa funksiyalar ötürərək istifadə edə bilirsiniz. Nəticədə kod “belə filtr edirik”, “elə çap edirik” kimi sonsuz variantlara parçalanmır — hər yerdə işləyən ümumi alətlərimiz var. Bu sayədə o, daha yığcam, anlaşılan və səhvlərə daha az meylli olur. Üstəlik, bu üslub müasir Stream API ilə əla uyğunlaşır, ona görə də axınlara keçəndə yenidən öyrənməyə ehtiyac qalmır — yanaşma eynidir.
6. Funksiyaları parametr kimi ötürərkən tipik səhvlər
Səhv № 1: İmza gözlənilən interfeysə uyğun gəlmir.
Əgər metod Predicate<User> qəbul edirsə, sizsə boolean yox, məsələn, String qaytaran lambda ötürürsünüzsə, kompilyator dərhal “ay-ay-ay!” deyəcək və layihəni yığmağa icazə verməyəcək. Qaytarılan dəyərin və parametrlərin gözlənilənlə uyğun gəldiyini yoxlayın.
Səhv № 2: Lambda dəyişə bilən dəyişənlərdən istifadə edir.
Java-da lambda-ifadələr yalnız xarici kontekstdən final və ya effektiv olaraq final olan dəyişənləri istifadə edə bilər. Belə dəyişəni lambdanın içində dəyişməyə çalışsanız — kompilyasiya xətası olacaq.
Səhv № 3: Interfeyslər arasında qarışıqlıq.
Bəzən məsələn, Consumer<T> ötürmək istəyirsiniz, amma təsadüfən nəsə qaytaran funksiya yazırsınız (məsələn, Function<T, R>). Lambda-nızın lazım olanı qaytardığını (və ya heç nə — Consumer üçün) yoxlayın.
Səhv № 4: Parametrlərdə həddən artıq mürəkkəb lambdalar.
Əgər lambda bir-iki sətirdən artıqdırsa, onu ayrıca dəyişənə və ya metoda çıxarmaq daha yaxşıdır. Əks halda kod oxunmaz olacaq və onda yalnız siz baş çıxaracaqsınız (o da həmişə yox).
GO TO FULL VERSION