17-bo‘lim

Optional va null xavfsizligi

NullPointerException sabablari, Optional yaratish va ishlatish, map va flatMap, Objects yordamchilari hamda null ni kamaytirish usullari.

🕑 15 daqiqa o‘qish 📄 783 so‘z 👁 2 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Muammo
  2. Yordamchi xato xabarlari
  3. Optional
  4. Qiymatni olish
  5. map va filter
  6. flatMap
  7. Optional ni qayerda ishlatish
  8. Objects yordamchilari
  9. Amaliy qoidalar
  10. Xulosa

null - "bu yerda hech narsa yo'q" degani. U foydali, lekin Java'dagi eng ko'p uchraydigan qulash sababi.

Muammo #

Java
public class Muammo {
    public static void main(String[] args) {
        String ism = null;

        try {
            System.out.println(ism.length());
        } catch (NullPointerException x) {
            System.out.println("Qulash: " + x.getClass().getSimpleName());
        }
    }
}
Natija
Qulash: NullPointerException
"Bir milliard dollarlik xato"

null tushunchasini 1965-yilda ingliz olimi Tony Hoare kiritgan. Keyinchalik u buni "mening bir milliard dollarlik xatom" deb atagan: uning hisobicha, null sabab bo'lgan qulashlar, zaifliklar va tuzatish ishlari o'nlab yillar davomida shuncha zarar keltirgan.

Java bu muammoni til darajasida hal qilmagan - String har doim null bo'lishi mumkin. Kotlin va boshqa keyingi tillar aynan shu kamchilikni tuzatish uchun yaratilgan.

Java'da bizda Optional va yaxshi odatlar bor.

Yordamchi xato xabarlari #

Java 14 dan NullPointerException xabari ancha aniqroq:

Java
public class Aniq {
    public static void main(String[] args) {
        Talaba t = new Talaba(null);

        try {
            System.out.println(t.manzil().shahar().toUpperCase());
        } catch (NullPointerException x) {
            System.out.println("Xabar: " + x.getMessage());
        }
    }
}

record Manzil(String shahar) {
}

record Talaba(Manzil manzil) {
}
Natija
Xabar: Cannot invoke "Manzil.shahar()" because the return value of "Talaba.manzil()" is null

Xabar qaysi chaqiruv null qaytarganini aniq aytadi. Eski Java'da faqat qator raqami bo'lardi va zanjirdagi qaysi bo'g'in aybdorligini topish qiyin edi.

Optional #

Optional<T> - "qiymat bo'lishi ham, bo'lmasligi ham mumkin" degan quticha:

Java
import java.util.Optional;

public class Yaratish {
    public static void main(String[] args) {
        Optional<String> bor = Optional.of("Namangan");
        Optional<String> bosh = Optional.empty();
        Optional<String> ehtimol = Optional.ofNullable(null);

        System.out.println("bor:     " + bor);
        System.out.println("bosh:    " + bosh);
        System.out.println("ehtimol: " + ehtimol);
        System.out.println("bor mavjudmi:  " + bor.isPresent());
        System.out.println("bosh bo'shmi:  " + bosh.isEmpty());
    }
}
Natija
bor:     Optional[Namangan]
bosh:    Optional.empty
ehtimol: Optional.empty
bor mavjudmi:  true
bosh bo'shmi:  true
MetodQachon
Optional.of(x)x aniq null emas
Optional.ofNullable(x)x null bo'lishi mumkin
Optional.empty()Qiymat yo'q
Optional.of(null) istisno tashlaydi
Java
import java.util.Optional;

public class Ogoh {
    public static void main(String[] args) {
        try {
            Optional.of(null);
        } catch (NullPointerException x) {
            System.out.println("of(null) quladi");
        }

        System.out.println("ofNullable(null): "
                + Optional.ofNullable(null));
    }
}
Natija
of(null) quladi
ofNullable(null): Optional.empty

of null ni qabul qilmaydi - u "men aniq bilaman, bu null emas" degan da'vo. Ishonchingiz komil bo'lmasa ofNullable ishlating.

Qiymatni olish #

Java
import java.util.Optional;

public class Olish {
    public static void main(String[] args) {
        Optional<String> bor = Optional.of("Namangan");
        Optional<String> bosh = Optional.empty();

        System.out.println(bor.orElse("sukut"));
        System.out.println(bosh.orElse("sukut"));
        System.out.println(bosh.orElseGet(() -> "hisoblangan sukut"));

        try {
            bosh.orElseThrow(() -> new IllegalStateException("qiymat yo'q"));
        } catch (IllegalStateException x) {
            System.out.println("orElseThrow: " + x.getMessage());
        }

        bor.ifPresent(q -> System.out.println("ifPresent: " + q));
        bosh.ifPresentOrElse(
                q -> System.out.println("bor: " + q),
                () -> System.out.println("ifPresentOrElse: qiymat yo'q"));
    }
}
Natija
Namangan
sukut
hisoblangan sukut
orElseThrow: qiymat yo'q
ifPresent: Namangan
ifPresentOrElse: qiymat yo'q
get() ni ishlatmang

Optional da get() metodi bor, lekin u qiymat yo'q bo'lsa NoSuchElementException tashlaydi:

Java
import java.util.Optional;

public class Get {
    public static void main(String[] args) {
        Optional<String> bosh = Optional.empty();

        try {
            System.out.println(bosh.get());
        } catch (Exception x) {
            System.out.println("get(): " + x.getClass().getSimpleName());
        }
    }
}
Natija
get(): NoSuchElementException

get() ishlatish Optional ning butun ma'nosini yo'qqa chiqaradi: siz null tekshiruvini Optional tekshiruviga almashtirdingiz, xolos.

Har doim orElse, orElseGet, orElseThrow yoki ifPresent ishlating.

orElse va orElseGet farqi: orElse argumentni har doim hisoblaydi, orElseGet esa faqat kerak bo'lganda. Sukut qiymat qimmat hisoblansa - orElseGet.

map va filter #

Optional ning asosiy kuchi shu yerda:

Java
import java.util.Optional;

public class Zanjir {
    public static void main(String[] args) {
        System.out.println(shaharKatta(new Talaba(new Manzil("Namangan"))));
        System.out.println(shaharKatta(new Talaba(null)));
        System.out.println(shaharKatta(new Talaba(new Manzil(null))));
        System.out.println(shaharKatta(null));
    }

    static String shaharKatta(Talaba t) {
        return Optional.ofNullable(t)
                .map(Talaba::manzil)
                .map(Manzil::shahar)
                .filter(s -> !s.isBlank())
                .map(String::toUpperCase)
                .orElse("(manzil yo'q)");
    }
}

record Manzil(String shahar) {
}

record Talaba(Manzil manzil) {
}
Natija
NAMANGAN
(manzil yo'q)
(manzil yo'q)
(manzil yo'q)

To'rt xil null holati - bitta zanjir, hech qanday if yo'q. map ichidagi funksiya null bo'lsa umuman chaqirilmaydi.

flatMap #

Metod o'zi Optional qaytarsa, map o'rniga flatMap kerak:

Java
import java.util.Optional;

public class FlatMap {
    public static void main(String[] args) {
        Ombor ombor = new Ombor();

        System.out.println(ombor.topish("Husanboy")
                .map(Talaba::ism)
                .orElse("topilmadi"));

        System.out.println(ombor.topish("Malika")
                .map(Talaba::ism)
                .orElse("topilmadi"));

        System.out.println(ombor.topish("Husanboy")
                .flatMap(Talaba::telefon)
                .orElse("telefon yo'q"));
    }
}

record Talaba(String ism, String raqam) {
    Optional<String> telefon() {
        return Optional.ofNullable(raqam);
    }
}

class Ombor {
    Optional<Talaba> topish(String ism) {
        if (ism.equals("Husanboy")) {
            return Optional.of(new Talaba("Husanboy", null));
        }
        return Optional.empty();
    }
}
Natija
Husanboy
topilmadi
telefon yo'q
map va flatMap farqi
Funksiya qaytaradiIshlatingNatija
Oddiy qiymat (String)mapOptional<String>
Optional<String>flatMapOptional<String>

map ni Optional qaytaruvchi funksiya bilan ishlatsangiz, natija Optional<Optional<String>> bo'lib qoladi - bu deyarli har doim xato.

Xuddi shu qoida Stream API da ham amal qiladi (16-bo'lim).

Optional ni qayerda ishlatish #

Maydon va parametr sifatida ishlatmang
Java
class Talaba {
    private Optional<String> telefon;         // YOMON
    void yoz(Optional<String> matn) { }       // YOMON
}

Uch sabab:

  1. Optional ning o'zi null bo'lishi mumkin - muammo hal bo'lmadi, faqat bir qavat qo'shildi;
  2. U Serializable emas - ba'zi freymvorklar bilan ishlamaydi;
  3. Har bir obyekt uchun qo'shimcha obyekt yaratiladi.

Optional ataylab qaytish qiymati uchun yaratilgan: "bu metod natija topmasligi mumkin" degan xabar.

JoyIshlatish
Metod qaytish turiHa - asosiy maqsadi
Sinf maydoniYo'q - oddiy maydon va null tekshiruvi
Metod parametriYo'q - ortiqcha yuklash yoki null
Kolleksiya elementiYo'q - bo'sh kolleksiya qaytaring

Objects yordamchilari #

Java
import java.util.Objects;

public class Yordamchi {
    public static void main(String[] args) {
        String bosh = null;
        String bor = "Namangan";

        System.out.println("isNull:       " + Objects.isNull(bosh));
        System.out.println("nonNull:      " + Objects.nonNull(bor));
        System.out.println("equals:       " + Objects.equals(bosh, bor));
        System.out.println("equals(null,null): "
                + Objects.equals(null, null));
        System.out.println("toString:     "
                + Objects.toString(bosh, "(yo'q)"));
        System.out.println("hash:         "
                + (Objects.hash("a", 1) == Objects.hash("a", 1)));
        System.out.println("requireNonNullElse: "
                + Objects.requireNonNullElse(bosh, "sukut"));

        try {
            Objects.requireNonNull(bosh, "ism berilishi shart");
        } catch (NullPointerException x) {
            System.out.println("requireNonNull: " + x.getMessage());
        }
    }
}
Natija
isNull:       true
nonNull:      true
equals:       false
equals(null,null): true
toString:     (yo'q)
hash:         true
requireNonNullElse: sukut
requireNonNull: ism berilishi shart
Argumentlarni konstruktorda tekshiring

Objects.requireNonNull ni konstruktor va ommaviy metodlar boshida ishlating:

Java
import java.util.Objects;

public class Tekshiruv {
    public static void main(String[] args) {
        System.out.println(new Talaba("Husanboy"));

        try {
            new Talaba(null);
        } catch (NullPointerException x) {
            System.out.println("Ushlandi: " + x.getMessage());
        }
    }
}

record Talaba(String ism) {
    Talaba {
        Objects.requireNonNull(ism, "ism null bo'lmasin");
    }
}
Natija
Talaba[ism=Husanboy]
Ushlandi: ism null bo'lmasin

Foydasi: xato kelib chiqish joyida aniqlanadi. Aks holda null obyekt ichida saqlanib, ancha keyin, butunlay boshqa joyda quladi - va sababini topish qiyin bo'ladi.

Amaliy qoidalar #

HolatNima qilish
Metod natija topmasligi mumkinOptional qaytaring
Ro'yxat bo'sh bo'lishi mumkinBo'sh ro'yxat qaytaring, null emas
Satr yo'q"" yoki Optional
Argument null bo'lmasligi kerakObjects.requireNonNull
Sukut qiymat bororElse yoki requireNonNullElse
MaydonOddiy tur, Optional emas
Bo'sh kolleksiya qaytaring
Java
import java.util.*;

public class Kolleksiya {
    public static void main(String[] args) {
        System.out.println("Yomon: "
                + (yomon("yo'q") == null ? "null keldi" : "ro'yxat"));
        System.out.println("Yaxshi: "
                + yaxshi("yo'q").size() + " ta element");

        for (String s : yaxshi("yo'q")) {
            System.out.println("bu hech qachon chiqmaydi: " + s);
        }
        System.out.println("Sikl xavfsiz o'tdi");
    }

    static List<String> yomon(String q) {
        return q.equals("bor") ? List.of("a") : null;
    }

    static List<String> yaxshi(String q) {
        return q.equals("bor") ? List.of("a") : List.of();
    }
}
Natija
Yomon: null keldi
Yaxshi: 0 ta element
Sikl xavfsiz o'tdi

Birinchi metoddan keyin har bir chaqiruvchi null ni tekshirishi kerak. Ikkinchisidan keyin natijani darhol for ga berish mumkin - bo'sh ro'yxat bo'ylab yurish xavfsiz.

Kolleksiyalar uchun Optional ham kerak emas - bo'sh kolleksiya o'zi "hech narsa yo'q" degani.

Amaliy topshiriq
  1. null o'zgaruvchiga murojaat qilib, xabar matnini o'qing.
  2. Ikki darajali zanjirda null qoldirib, xabar qaysi bo'g'inni ko'rsatishini ko'ring.
  3. Optional.of, ofNullable va empty ni yarating.
  4. Optional.of(null) yozib, istisnoni ko'ring.
  5. orElse, orElseGet va orElseThrow ni sinang.
  6. get() ni bo'sh Optional da chaqirib ko'ring.
  7. map zanjiri bilan ichma-ich null larni xavfsiz o'ting.
  8. flatMap bilan Optional qaytaruvchi metodni bog'lang.
  9. Objects.requireNonNull ni record konstruktorida ishlating.
  10. null qaytaruvchi metodni bo'sh ro'yxat qaytaradigan qilib qayta yozing.

Xulosa #

  • Java 14 dan NullPointerException xabari qaysi chaqiruv null qaytarganini aytadi.
  • Optional.of null qabul qilmaydi - ofNullable ishlating.
  • get() ni ishlatmang - orElse, orElseGet, orElseThrow yoki ifPresent.
  • map zanjiri null larni xavfsiz o'tkazadi; funksiya Optional qaytarsa - flatMap.
  • Optional faqat qaytish turi uchun; maydon va parametrda ishlatmang.
  • Objects.requireNonNull bilan argumentlarni kelib chiqish joyida tekshiring.
  • Kolleksiya uchun Optional emas - bo'sh kolleksiya qaytaring.

Keyingi bo'limda fayllar bilan ishlashni o'rganamiz.

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.