15-bo‘lim
Lambda va funksional interfeyslar
Lambda ifodalar, funksional interfeys tushunchasi, Function va Consumer oilasi, metod havolalari, yopilmalar va effektiv final qoidasi.
Ushbu bo‘lim mundarijasi
Lambda - nomsiz funksiyaning qisqa yozuvi. Java 8 dan boshlab u tilning eng ko'p ishlatiladigan vositalaridan biriga aylandi.
Lambda'gacha #
Java 8 gacha "kodni parametr sifatida uzatish" uchun anonim sinf yozilardi:
import java.util.*;
public class Eski {
public static void main(String[] args) {
List<String> shaharlar = new ArrayList<>(
List.of("Samarqand", "Chust", "Namangan"));
shaharlar.sort(new Comparator<String>() {
@Override
public int compare(String a, String b) {
return Integer.compare(a.length(), b.length());
}
});
System.out.println(shaharlar);
}
}
[Chust, Namangan, Samarqand]
Bitta amal uchun olti qator. Lambda bilan:
import java.util.*;
public class Yangi {
public static void main(String[] args) {
List<String> shaharlar = new ArrayList<>(
List.of("Samarqand", "Chust", "Namangan"));
shaharlar.sort((a, b) -> Integer.compare(a.length(), b.length()));
System.out.println("Uzunlik bo'yicha: " + shaharlar);
shaharlar.sort(Comparator.comparing(String::length).reversed());
System.out.println("Teskari: " + shaharlar);
shaharlar.sort(Comparator.naturalOrder());
System.out.println("Alifbo bo'yicha: " + shaharlar);
}
}
Uzunlik bo'yicha: [Chust, Namangan, Samarqand]
Teskari: [Samarqand, Namangan, Chust]
Alifbo bo'yicha: [Chust, Namangan, Samarqand]
Funksional interfeys #
Lambda faqat bitta abstrakt metodi bor interfeys uchun yoziladi. Bunday interfeysga funksional interfeys deyiladi.
public class Funksional {
public static void main(String[] args) {
Hisoblovchi qoshish = (a, b) -> a + b;
Hisoblovchi kopaytirish = (a, b) -> a * b;
Hisoblovchi kattasi = (a, b) -> a > b ? a : b;
System.out.println("Qo'shish: " + hisobla(7, 5, qoshish));
System.out.println("Ko'paytirish: " + hisobla(7, 5, kopaytirish));
System.out.println("Kattasi: " + hisobla(7, 5, kattasi));
}
static int hisobla(int a, int b, Hisoblovchi amal) {
return amal.bajar(a, b);
}
}
@FunctionalInterface
interface Hisoblovchi {
int bajar(int a, int b);
}
Qo'shish: 12
Ko'paytirish: 35
Kattasi: 7
@FunctionalInterface izohini yozingU majburiy emas, lekin foydali: agar interfeysga ikkinchi abstrakt metod qo'shilsa, kompilyator darhol xato beradi:
error: Unexpected @FunctionalInterface annotation
multiple non-overriding abstract methods found
Usiz esa xato faqat lambda yozilgan joyda, ancha tushunarsiz shaklda chiqadi.
default va static metodlar hisobga olinmaydi - ular
qancha bo'lsa ham interfeys funksional bo'lib qolaveradi.
Tayyor funksional interfeyslar #
Amalda o'z interfeysingizni yozish kamdan-kam kerak -
java.util.function paketida tayyorlari bor:
import java.util.function.*;
public class Tayyor {
public static void main(String[] args) {
Function<Integer, Integer> kvadrat = son -> son * son;
Predicate<String> uzunmi = matn -> matn.length() > 5;
Consumer<String> yozuvchi =
matn -> System.out.println("[LOG] " + matn);
Supplier<String> manba = () -> "yangi qiymat";
BiFunction<Integer, Integer, Integer> qoshish = (a, b) -> a + b;
UnaryOperator<String> katta = String::toUpperCase;
System.out.println("Function: " + kvadrat.apply(7));
System.out.println("Predicate: " + uzunmi.test("Namangan"));
yozuvchi.accept("xabar");
System.out.println("Supplier: " + manba.get());
System.out.println("BiFunction:" + qoshish.apply(3, 4));
System.out.println("Unary: " + katta.apply("salom"));
}
}
Function: 49
Predicate: true
[LOG] xabar
Supplier: yangi qiymat
BiFunction:7
Unary: SALOM
| Interfeys | Oladi | Qaytaradi | Metod |
|---|---|---|---|
Function<T,R> | T | R | apply |
BiFunction<T,U,R> | T, U | R | apply |
Predicate<T> | T | boolean | test |
Consumer<T> | T | hech narsa | accept |
Supplier<T> | hech narsa | T | get |
UnaryOperator<T> | T | T | apply |
BinaryOperator<T> | T, T | T | apply |
Function<Integer, Integer> ishlatsangiz, har chaqiruvda
avtoqutilash bo'ladi: int → Integer → int.
Million element uchun bu sezilarli sekinlashtiradi. Shuning uchun primitivlar uchun alohida interfeyslar bor:
| Umumiy | Primitiv versiyasi |
|---|---|
Function<Integer, Integer> | IntUnaryOperator |
Predicate<Integer> | IntPredicate |
Supplier<Integer> | IntSupplier |
Function<Integer, R> | IntFunction<R> |
Kichik ro'yxatlarda farq sezilmaydi. Katta hajmda esa ularni ishlating.
Lambda yozilish shakllari #
import java.util.function.*;
public class Shakllar {
public static void main(String[] args) {
Function<Integer, Integer> a = son -> son * 2;
Function<Integer, Integer> b = (son) -> son * 2;
Function<Integer, Integer> c = (Integer son) -> son * 2;
Function<Integer, Integer> d = son -> {
int natija = son * 2;
return natija;
};
Supplier<String> parametrsiz = () -> "hech narsa olmaydi";
BiFunction<Integer, Integer, Integer> ikkita = (x, y) -> x + y;
System.out.println(a.apply(5) + ", " + b.apply(5)
+ ", " + c.apply(5) + ", " + d.apply(5));
System.out.println(parametrsiz.get());
System.out.println(ikkita.apply(3, 4));
}
}
10, 10, 10, 10
hech narsa olmaydi
7
| Shakl | Qachon |
|---|---|
son -> ifoda | Bitta parametr, bitta ifoda - eng ko'p ishlatiladi |
(a, b) -> ifoda | Bir nechta parametr |
() -> ifoda | Parametrsiz |
son -> { ... } | Bir nechta ko'rsatma, return majburiy |
Metod havolalari #
Lambda faqat mavjud metodni chaqirsa, uni qisqaroq yozish mumkin:
import java.util.*;
import java.util.function.*;
public class Havolalar {
public static void main(String[] args) {
List<String> shaharlar = new ArrayList<>(
List.of("namangan", "andijon", "samarqand"));
shaharlar.forEach(System.out::println);
Function<String, Integer> uzunlik = String::length;
System.out.println("Uzunlik: " + uzunlik.apply("Namangan"));
Supplier<ArrayList<String>> yaratuvchi = ArrayList::new;
System.out.println("Yangi ro'yxat: " + yaratuvchi.get());
Function<String, String> katta = String::toUpperCase;
System.out.println("Katta: " + katta.apply("salom"));
BiFunction<String, String, Boolean> boshlanadimi = String::startsWith;
System.out.println("Boshlanadimi: "
+ boshlanadimi.apply("Namangan", "Nam"));
}
}
namangan
andijon
samarqand
Uzunlik: 8
Yangi ro'yxat: []
Katta: SALOM
Boshlanadimi: true
| Shakl | Ma'nosi | Lambda ekvivalenti |
|---|---|---|
Sinf::statikMetod | Statik metod | x -> Sinf.statikMetod(x) |
obyekt::metod | Aniq obyekt metodi | x -> obyekt.metod(x) |
Sinf::metod | Birinchi argument obyekt bo'ladi | x -> x.metod() |
Sinf::new | Konstruktor | () -> new Sinf() |
String::length ni shunday o'qing: "berilgan satrning length()
metodini chaqir".
Ya'ni lambda parametri obyektga aylanadi, argumentga emas.
String::startsWith esa ikki argument oladi: birinchisi obyekt,
ikkinchisi metod argumenti. Shuning uchun u BiFunction ga mos
keladi.
Yopilmalar va effektiv final #
import java.util.function.Supplier;
public class Yopilma {
public static void main(String[] args) {
String salom = "Assalomu alaykum";
Supplier<String> xabar = () -> salom + ", dunyo!";
System.out.println(xabar.get());
System.out.println("Ikkinchi marta: " + xabar.get());
}
}
Assalomu alaykum, dunyo!
Ikkinchi marta: Assalomu alaykum, dunyo!
Java'da lambda ichida ishlatilgan lokal o'zgaruvchi final yoki
effektiv final bo'lishi shart - ya'ni u yaratilgandan keyin
o'zgarmasligi kerak.
int hisoblagich = 0;
Runnable r = () -> System.out.println(hisoblagich);
hisoblagich++; // XATO
error: local variables referenced from a lambda expression
must be final or effectively final
Sabab: lambda o'zgaruvchining nusxasini oladi. Agar asl o'zgaruvchi keyin o'zgarsa, lambda eski qiymatni ko'rardi - bu chalkashlik tug'diradi. Java buni umuman taqiqlagan.
Yechim: o'zgaruvchan holat kerak bo'lsa, maydondan yoki bir elementli massivdan foydalaning:
public class Aylanma {
public static void main(String[] args) {
int[] hisoblagich = {0};
Runnable oshir = () -> hisoblagich[0]++;
oshir.run();
oshir.run();
oshir.run();
System.out.println("Hisoblagich: " + hisoblagich[0]);
}
}
Hisoblagich: 3
Massivning o'zi o'zgarmadi (havola bir xil qoldi), faqat uning ichidagi qiymat o'zgardi - shuning uchun bu ruxsat etiladi.
Diqqat: bu usul ko'p oqimli kodda xavfli. U faqat bitta oqimda ishlaydi.
Funksiyalarni birlashtirish #
import java.util.function.*;
public class Birlashtirish {
public static void main(String[] args) {
Function<Integer, Integer> ikkiBarobar = son -> son * 2;
Function<Integer, Integer> birQoshish = son -> son + 1;
System.out.println("andThen: "
+ ikkiBarobar.andThen(birQoshish).apply(5));
System.out.println("compose: "
+ ikkiBarobar.compose(birQoshish).apply(5));
Predicate<String> uzun = m -> m.length() > 5;
Predicate<String> nBilan = m -> m.startsWith("N");
System.out.println("and: " + uzun.and(nBilan).test("Namangan"));
System.out.println("or: " + uzun.or(nBilan).test("Nur"));
System.out.println("negate: " + uzun.negate().test("Nur"));
}
}
andThen: 11
compose: 12
and: true
or: true
negate: true
andThen - avval o'zi, keyin keyingisi. compose - teskarisi.
Shuning uchun 5 * 2 + 1 = 11, lekin (5 + 1) * 2 = 12.
- Anonim sinf bilan
Comparatoryozing, keyin uni lambda'ga aylantiring. - O'z funksional interfeysingizni yozing va
@FunctionalInterfaceqo'ying. - Unga ikkinchi abstrakt metod qo'shib, xatoni ko'ring.
Function,Predicate,ConsumervaSupplierni sinab ko'ring.- Lambda'ning to'rt xil shaklini yozing.
String::lengthvaString::toUpperCasemetod havolalarini ishlating.ArrayList::newbilanSupplieryarating.- Lambda ichida o'zgaruvchini o'zgartirishga urinib, xatoni ko'ring.
- Bir elementli massiv bilan hisoblagich yozing.
andThenvacomposenatijalarini solishtiring.
Xulosa #
- Lambda - bitta abstrakt metodi bor interfeys uchun yoziladi.
@FunctionalInterfaceizohi xatoni erta topadi.- Asosiy tayyor interfeyslar:
Function,Predicate,Consumer,Supplier. - Katta hajmda primitiv versiyalarni ishlating - avtoqutilash qimmat.
- Metod havolasi (
String::length) lambda'ning qisqa shakli. Sinf::metodshaklida lambda parametri obyektga aylanadi.- Lambda ichidagi lokal o'zgaruvchi effektiv
finalbo'lishi shart. andThenvacomposefunksiyalarni zanjirga bog'laydi.
Keyingi bo'limda Stream API ni o'rganamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.