Java 8: Optional prieš null    

Taip pat siūlome susipažinti: Anotacijos Java kalboje  
Lambda išraiškos – Java į naują lygį  

Kuris iš Java programuotojų nėra gavęs NullPointerException? Ir tikriausiai dažnas jų pagalvoja: „Nu, jo, tai galvos skausmas kiekvienam programuotojui, naujokui ir patyrusiam, ir čia daug nieko nepadarysi – tai kaina, kurią turim sumokėti už šį patogų, tačiau kaprizingą null konstruktą“. Kokios jo ištakos?
null 1965 m. įvedė britas Tony Hoare projektuodamas ALGOL W, vieną pirmųjų programavimo kalbų, naudojančių tipus, turinčią įrašų išskyrimą „iš krūvos“ – nes „tai taip lengva realizuoti“. Priešingai savo iškeltam tikslui „užtikrinti, kad visos nuorodos būtų absoliučiai saugios, automatiškai patikrinamos kompiliatoriaus“, jis nusprendė išimtį padaryti dėl null, nes manė, kad tai patogausias būdas nurodyti reikšmės nebuvimą. Po daugelio metų jis pasmerkė tą savo sprendimą, pavadindamas jį „milijardo vertės klaida“ – ir mes visi regim jos poveikį mums.

Tad ir pabandykime iš arčiau pažiūrėti, kokias problemas sukelia null.
Paimkim įdėtinių klasių rinkinį:

class Person {
	private Car car;
	public Car getCar() { return car; }
}

class Car {
	private Insurance insurance;
	public Insurance getInsurance() { return insurance; }	    	
}

class Insurance {
	private String name;
	public String getName() { return name; }
}

Bet kas blogai realizavus tokį metodą?

String getCarInsurance(Person person) {
	 return person.getCar().getInsurance().getName();
}

Atrodo labai normaliai. Tačiau ne visi asmenys turi automobilius, o kai kurie jų ir nedraudžia (nors ir privalėtų!). Tad koks bus getCar() rezultatas? Nelaimei, bendra praktika yra gražinti null reikšmę – tuo pažymint automobilio neturėjimą. Bet tokiu atveju tolimesnis vykdymas sukels NullPointerException situaciją. Bet ir tai dar ne viskas – o kas, jei ir person reikšmė yra null (o dar yra ir getInsurance(), taip pat galintis gražinti null)?

Kaip apsisaugoti nuo to? Paprastai, visur, kur būtina (o kartais, apsidraudžiant, net ir kur nebūtina), įdedami null patikrinimai.

Tad pirmas bandymas perrašyti „nesaugų“ metodą būtų:

String getSafeCarInsurance(Person person) {
	if (person != null) {
		Car car = person.getCar();
		if (car != null) {
			Insurance insurance = car.getInsurance();
		    	if (insurance != null) return insurance.getName();
		}
	}
         return "Nenustatyta";
}

Šį variantą pavadinkime „gilia abejone“, nes jis parodo pasikartojantį elgesį – kaskart suabejojama, ar kintamasis turi reikšmę. Galiausiai gauname griozdišką kelių lygių kodą. Tad pabandykime kitaip:

String getCarInsuranceUsingExits(Person person) {
        if (person == null) return "Nenustatyta";
	Car car = person.getCar();
        if (car == null) return "Nenustatyta";
	Insurance insurance = car.getInsurance();
	if (insurance == null) return "Nenustatyta";
	return insurance.getName();
}

Bet ir šis sprendimas tolimas nuo idealo: metode yra 4-i išėjimo taškai, iš jų triskart gražina reikšmę "Nenustatyta" – ir gerai, jei nesuklysime parašydami tą reikšmę... na gerai, galite ją priskirti konstantai ir naudoti šios vardą. O toliau – klaidoms imlus kodas – o kas, jei pamiršime kuriai nors iš savybių patikrinti null reikšmę?!

Ir toks „sprendimas“ tėra purvo išpylimas ant kilimo. Jis nesprendžia klaidos kode, o tik maskuoja jos pasekmes, padarydamas jos suradimą sunkesniu ir ilgiau trunkančiu. O ir klaida gali pasireikšti tik po kelių mėnesių...

Taigi, atrodo, kad null naudojimas nesančios reikšmės žymėjimui nėra geriausias būdas. O ką galime rinktis? Pasižvalgykime į kitas kalbas...

Prieš kelis metus tokios kalbos kaip Groovy šią problemą sprendė įvesdamos saugios navigacijos operatorių ?. Pažvelkime, kaip Groovy kalboje būtų apeita minėta situacija:

def carInsurance = person?.car?.insurance?.name

Ką atlieka ši išraiška yra neabejotinai aišku. Pasitaikiusi null reikšmė „nuburbuliuoja“ iki rezultato. Panaši galimybė buvo pasiūlyta Java 7, tačiau jos buvo atsisakyta.

Kitos funkcionalinės kalbos (Haskell ar Scala) elgiasi kitaip. Haskell įtraukia Maybe tipą, iš esmės uždarantį nebūtiną reikšmę; ir nėra null koncepto. Scala turi panašią Option[T] konstrukciją, leidžiančią nebūtiną T tipo reikšmę; jos nebuvimą reikalaujama patikrinti, tad tai tik sustiprina null patikrinimo idėją.

Nagi, visa tai atrodo gana abstrakčiai. Java 8 irgi pasiima „nebūtinos“ reikšmės idėją per naują java.util.Optional<T> klasę. Jei objektas turi reikšmę, tiesiog „apvynioja“ jį. Reikšmės nebuvimas modeliuojamas Optional.empty() metodu. Tai „fabrikinis“ metodas, gražinantis Optional klasės specialų singletono įkūnijimą. Bet kuo tai skiriasi nuo null?

Semantiškai tai gali atrodyti kaip tas pats dalykas, tačiau praktiškoje skirtumas milžiniškas. Kreipimasis per null neišvengiamai iššauks išimtį, o Optional.empty() yra validus, naudotinas Optional tipo objektas. Netrukus parodysime, kaip.

Pirmiausia naujai susikuriame mūsų minėtas klases:

private class Person {	
	private Optional<Car> car;	    
	public Optional<Car> getCar() { return car; }
}
	
private class Car {
         private Optional<Insurance> insurance;
         public Optional<Insurance> getInsurance() { return insurance; }
}
    
private class Insurance {
         private String name;
         public String getName() { return name; }
}

O toliau pažiūrim, kaip susikuriamos Optional tipo objektai. Pirmas būdas – naudoti Optional.empty() metodą:

Optional car = Optional.empty();

Toliau, galima sukurti „opcinę“ reikšmę iš turinčio reikšmę objekto (bet jei car reikšmė bus null, iškart gausime NullPointerException):

Optional<Car> optCar = Optional.of(car);

Ankstesnio minėto atvejo su null išvengimui galima panaudoti:

Optional<Car> optCar = Optional.ofNullable(car);

Papildomas perspėjimas: vis tik Optional.of metodo parametro reikšmė negali būti null - tokiu atveju gausime NullPointerException išimtį, pvz.,

String vardas = null; 
Optional<String> optVardas = Optional.of(vardas);

Tačiau tokiu atveju mes galime naudoti:

Optional<String> optVardas = Optional.ofNullable(vardas);

kas priskiria „tuščią“ Optional objektą.

O ... kaip gauti Optional tipo reikšmę? Tam yra get metodas, tačiau jis iššaukia išimtį, kai objektas neturi reikšmės, tad jo naudojimas veda į visus anksčiau minėtus aspektus.

Tipinis veiksmas yra gauti informaciją iš objekto. Tarkim:

String name = null;
if ( insurance != null ) {
    name = insurance.getName();
}

Tam tikslui Optional turi map metodą, pvz.:

Insurance insurance = new Insurance();
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);

Konceptualiai tai panašu į stream naudojamą map metodą, kur jis taiko numatytą funkciją kiekvienam srauto elementui. Tad Optional objektą galime įsivaizduoti kaip duomenų rinkinį, teturintį daugų daugiausia vieną elementą. Jei tasai turi elementą, funkcija jį perduota į map, kad toji transformuotų reikšmę. Jei objektas tuščias, niekas nenutinka.

Papildoma pastaba: map metodas gražina skaičiavimo „įvyniotame“ Optional  rezultatą (Optional tipo), o tada galima panaudoti reikiamą metodą reikšmės gavimui sugražintame Optional objekte. Tai pailiustruosime tokiu pavyzdžiu:

List<String> companies = Arrays.asList ("IBM", "Oracle", "Microsoft", "");
Optional<List<String>> optCompanies = Optional.of(companies);
	
System.out.println("Kompanijos: " + app.getListLength(optCompanies));
optCompanies = Optional.empty();
System.out.println("Kiekis kai empty Optional: " + app.getListLength(optCompanies));
// ...
public int getListLength(Optional<List<String>> pList) {
  return pList.map(List::size).orElse(0);
}

Pirmo kreipinio metu perduodamas kompanijų sąrašas, tada įvykdoma List metodas size() (sąrašo ilgis) ir gražinamas Optional metodas šiai reikšmei. Tada orElse gražina šią sąrašo reikšmę (4).
Antro kreipinio metu perduodamas „tuščias“ Optional objektas, - šiuo atveju map sukuria „tuščią“ Optional objektą, ir šiam orElse gražina alternatyvią reikšmą, t.y. 0.

Bet vis tik, o kaip mums perrašyti mums reikiamą getCarInsurance funkciją?

Mums gali kilti noras parašyti tokį kodą:

Optional<Person> optPerson = Optional.of(person);
Optional<String> name =
    optPerson.map(Person::getCar)
           .map(Car::getInsurance)
           .map(Insurance::getName);

Gaila, jis nebus kompiliuojamas. Nors optPeople yra Optional<People> tipo, getCar() gražina Optional<Car> tipo reikšmę, tad map rezultatas bus Optional<Optional<Car>> tipo, o tada getInsurance kreipinys yra neleistinas dėl tipų nesuderinamumo. Ką daryti?

Pasižiūrėkime į dar vieną Optional funkciją – flatMap, kurią taipogi turi ir srautai. Ji tarsi „suplokština“ map rezultatą. Tad rašom:

String getCarInsurance(Optional<Person> person) {
	    return person.flatMap(Person::getCar)
	            .flatMap(Car::getInsurance)
	            .map(Insurance::getName)
	            .orElse ("Nenustatyta");
}

Gavome siekiamą tikslą nenaudodami daugybės if. Taip pat šis sprendimas vaiskus tipų požiūriu – aiškiai įvardija, kad reikšmės gali ir nebūti. Jis gali būti grafiškai iliustruotas taip:
PasaulėflatMap procese

Taigi, parodėme, kaip elgtis su objektais, galinčiais neturėti reikšmės. Tačiau Optional klasė buvo kuriama kitais sumetimais. Pvz., Java kalbos architektas Brian Goetz'as aiškiai teigia, kad Optional skirta tik opcinio gražinimo idiomai. Tad, kadangi nemanyta jos naudoti lauko tipui, ji neįdiegia Serializable interfeiso. Tad tokio poreikio užtikrinimui realizuoti papildomą metodą, gražinantį „neprivalomą“ reikšmę, pvz.,

public Optional<Car> getCarOptionally() { return 
Optional.ofNullable(car); }

Nutylimosios reikšmės ir Optional „išvyniojimas“

Optional klasė numato kelis metodus reikšmės nuskaitymui:

* get – paprasčiausias, tačiau nesaugiausias reikšmės paėmimo būdas. Jis „išmeta“ NoSuchElementException išimtį, jei objektas yra tuščias. Tad su juo netoli pasistūmėsim lyginant su null patikromis;
* orElse(T other) - jau mūsų naudotas metodas. Jis numato nutylimąją reikšmę, kai objektas yra tuščias;
* orElseGet(Supplier<? extends T> other) – „tinginio variantas“. Supplier iškviečiamas tik objektui neturint reikšmės. Jis naudotinas, kai nutylimoji reikšmė kuriama lėtai (trputis ekonomijos) arba norite būti užtikrinti, kad tai bus padaryta tik jei objektas tuščias (šiuo atveju tai griežtai būtina);
* orElseThrow(Supplier<? extends X> exceptionSupplier) – panašus į get, tačiau leidžia pasirinkti išimties tipą;
* ifPresent(Consumer<? super T> consumer) – nurodo, kad reikia atlikti veiksmą su turima reikšme. Nieko nedaroma, jei reikšmės nėra.

Tarkim, reikia metodo su painia logika, kuris pagal nurodytą asmenį ir automobilį, surastų pigiausią draudimo kompaniją. Jo skeletas būtų toks:

public Insurance pigiausiasDraudikas(Person person, Car car) {
	Insurance piguva = null;
	// veiksmai
	return piguva;
}

O jei norime, kad jis būtų saugus null atžvilgiu, parengiam tokį jo „įvyniojimą“:

public Optional<Insurance> pigiausiasDraudikasSaugiai(Optional<Person> person, Optional<Car> car) {
    if (person.isPresent() && car.isPresent()) 
    	return Optional.of(pigiausiasDraudikas(person.get(), car.get()));
    
    return Optional.empty();
}

Bet tokia realizacija pernelyg artima null patikroms. Ar nėra geresnio būdo?
Prisiminus ankstesnį flatMap ir map kombinavimą, perrašom elegantiškai:

public Optional<Insurance> pigiausiasDraudikasLambda(Optional<Person> person, Optional<Car> car) {
	return person.flatMap(p -> car.map(c -> pigiausiasDraudikas(p, c)));
}

Šiuo atveju lambda išraiška nebus vykdoma, jei Person objektas yra tuščias. O lambda išraiškos kūne, jei nėra jokio automobilio, gražinamas tuščias Optional objektas.

Panašumai su srautais dar nesibaigė. Bendras su jais yra ir filter metodas. Jį panaudosime atrenkant draudikus, kurių pavadinimas yra "Drauda":

optInsurance.filter(dr -> dr.getName().equals("Drauda"))
         .ifPresent(x -> System.out.println("ok"));

O dabar tarkim, kad asmens klasė turi metodą getAge(), pateikiantį asmens amžių. Adaptuokime kompanijos paieškos metodą, atsižvelgiant į draudžiamojo amžių:

public String getCarInsuranceOnAge(Optional<Person> person, int minAge) {
    return person.filter(p -> p.getAge() >= minAge)
                 .flatMap(Person::getCar)
                 .flatMap(Car::getInsurance)
                 .map(Insurance::getName)
                 .orElse( „Nenustatyta“ );
}

Apibendrinant galima pasakyti, kad Optional klasė priverčia permąstyti, kaip elgtis su potencialiai negarantuotomis reikšmėmis. Tai gali paveikti ir sąveiką su pačia Java API. Vienok, suderinamumui su senuoju kodu, Java API liko nepakeista. Bet ir čia mes galime sužaisti, „apvyniodami“ tokius API kreipinius. Tarkim, map reikšmė tebegražina null, jei rakto reikšmė nesurandama.

Panaudoti Optional galima ir išimčių valdyme. Tarkim, teksto eilutės vertimas į sveiką skaičių iššaukia NumberFormatException išimtį, jei eilutėje nėra „išgliaudomo“ skaičiaus. Išsprendžiama apgaubiant:

public static Optional<Integer> stringToInt (final String pStr) {
	try {
		return Optional.of(Integer.parseInt(pStr));
	} catch ( NumberFormatException e ) { 
		return Optional.empty();
	}
}

Beje, kaip ir srautai, Optional turi savas variacijas: OptionalInt, OptionalLong ir OptionalDouble. Jas jau paminėjime pristatydami srautus lambda išraiškose. Tačiau neskatintumėm jų naudoti, nes iš jų „iškastruoti“ map, flatMap ir filter metodai.

Vis tik – elkitės atsargiai

Šio straipsnelio pavyzdžiuose kai kurie metodai parametruose naudojo Optional tipą (getCarInsurance, pigiausiasDraudikasSaugiai, pigiausiasDraudikasLambda, getCarInsuranceOnAge). Tačiau iš tikro jo įtraukimo į Java intencija buvo jį panaudoti kaip return, o ne parametro tipą.

Minėtų metodų atveju turime pavojų, kad jie bus iškviesti neteisingai. Įsivaizduokite, kad jūs sukūrėte straipsnyje minėtą getCarInsuranceOnAge metodą, o kitas programuotojas jį iškvietė taip:

Person person = null;
String getCarInsuranceOnAge(person, 25);

Aišku, jis tada gautų NullPointerException ir jus išvadintų pačiais „gražiausiais“ žodžiais. Tad Optional naudojimas parametrams net nėra rekomenduojamas kai kurių kodo analizatorių.

Apibendrinant

O dabar sudėkime viską, ką sužinojome. Tam susikurkime savybių (Properties) rinkinį. Tarkim, kad jos perteikia trukmę sekundėmis – tad tai turi būti sveikas skaičius. Metodas kiekTrunka turi pateikti savybės reišmę, bet jei ši nėra sveikas skaičius, gražinti 0.

Pavyzdį realizuojame kaip klasę:

private class Savybes {
	Properties props = new Properties();
	// ...	
	public int kiekTrunka (String pName) {		
		return Optional.ofNullable(props.getProperty(pName))
                .flatMap(LabasOptional::stringToInt)
                .filter(i -> i > 0)
                .orElse(0);		
	}
}

Pilnas veikiantis kodas, iliustruojantis straipsnelyje aptariamus klausimus, pateikiamas prieduose. Jų vykdymui naudokite Java 8 aplinką.

Pastaba: Java 9  papildė Optional naujais metodais:

  • or - leidžiantį esant „tuščiam“ Optional  metodui numatyti kitą reikšmę;
  • ifPresentOrElse - leidžiantį atlikti atskirus veiksmus, kai turima reikšmė ir kai neturima;
  • stream - konvertuojantį Optional  į „srautą“.

  • Priedai

    1. Žaidimas su null

    Pateikiame pilną pradinį pavyzdžio, iliustruojančio straipsnyje aprašytas problemas su null, tekstą.

    Pastaba: Jo vykdymui naudokite Java 8 aplinką.

    public class ProcedeNULL {
    	
    public static void main(String[] args) {
    	System.out.println("Labas, Optional!/n");
    
    	ProcedeNULL app = new ProcedeNULL();
    	Person petras = app.new Person();
    	try {
    	   System.out.println(getCarInsurance(petras));
    	} catch (Exception e) {
    	   System.out.println("Gavom NPE");
        }
        System.out.println(getSafeCarInsurance(petras));
        System.out.println(getCarInsuranceUsingExits(petras));
           
        System.out.println("\nSudie, Optional!");
    }
    
    static String getCarInsurance(Person person) {
    	    return person.getCar().getInsurance().getName();
    }
    
    static String getSafeCarInsurance(Person person) {
    	 if (person != null) {
    	    Car car = person.getCar();
    	    if (car != null) {
    	    	Insurance insurance = car.getInsurance();
    	    	if (insurance != null) return insurance.getName();
    	    }
    	 }
    	 return "Nenustatyta";
    }
    
    static String getCarInsuranceUsingExits(Person person) {
    	 if (person == null) return "Nenustatyta";
    	 Car car = person.getCar();
    	 if (car == null) return "Nenustatyta";
    	 Insurance insurance = car.getInsurance();
    	 if (insurance == null) return "Nenustatyta";
    	 return insurance.getName();
    }
    
    // --------------------------------------------------------------------
    private class Person {	
    	private Car car;	    
    	public Car getCar() { return car; }
    }
    	
    private class Car {
        private Insurance insurance;
        public Insurance getInsurance() { return insurance; }
    }
        
    private class Insurance {
        private String name;
        public String getName() { return name; }
    }
    	
    }

    Jos vykdymo rezultatai:

    Labas, Optional
    
    Gavom NPE
    Nenustatyta
    Nenustatyta
    
    Sudie, Optional

    2. Susipažinkime su Optional

    Pateikiame pilną pradinį pavyzdžio, iliustruojančio straipsnyje aprašytas problemas su null, tekstą.

    Pastaba: Jo vykdymui naudokite Java 8 aplinką. Vykdymo rezultatai – pabaigoje.

    import java.util.Optional;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import java.util.Properties;
    
    public class LabasOptional {
    
    final static String coUnsetText = "Nenustatyta";
    
    public static void main(String[] args) {
    	System.out.println("Labas, Optional\n");
    	
    	LabasOptional app = new LabasOptional();
    	Person petras = app.new Person();
    	Optional<Person> mikas = Optional.ofNullable(petras);
    	
    	
    	Insurance insurance = app.new Insurance();
    	insurance.setName("Drauda");
    	Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
    	
    	Optional<String> name = optInsurance.map(Insurance::getName);
    	
    	System.out.println(getCarInsurance(mikas));
    
    	
    	optInsurance.filter(dr -> dr.getName().equals("Drauda"))
    	            .ifPresent(x -> System.out.println("ok"));
    
    
    	Map<String, String> map = new HashMap<String, String>();
    	map.put("spyna", "kambarys");
    	
    	Optional<Object> value = Optional.ofNullable(map.get("raktas"));
    	
    	System.out.println("Gavom: " + value);
    	
    	String str = "melagis";	
    	System.out.println("Sveikasis: " + stringToInt(str) );
    	
    	Savybes sav = app.new Savybes();
    	
    	sav.add("vienas", "1");
    	sav.add("true", "true");
    	sav.add("minustrys", "-3");
    	sav.add("penki", "5");
    	
    	System.out.println("vienas trunka: " + sav.kiekTrunka("vienas"));
    	System.out.println("true trunka: " + sav.kiekTrunka("true"));	
    	System.out.println("minustrys trunka: " + sav.kiekTrunka("minustrys"));	
    	System.out.println("keturi trunka: " + sav.kiekTrunka("keturi"));
    	System.out.println("penki trunka: " + sav.kiekTrunka("penki"));
    		
    	System.out.println("\nPapildomai:");
    	app.testNPE();
    	try {
    		app.getCarInsuranceOnAge(mikas, null);
    	} catch (Exception e) {
    		System.out.println("NPE, kai kviesta su Optional parametru lygiu null");
    	}
    
    	List<String> companies = Arrays.asList ("IBM", "Oracle", "Microsoft", "");
    	Optional<List<String>> optCompanies = Optional.of(companies);
    	
    	System.out.println("Kompanijos: " + app.getListLength(optCompanies));
    	optCompanies = Optional.empty();
    	System.out.println("Kiekis kai empty Optional: " + app.getListLength(optCompanies));
    	
    	System.out.println("\nSudie, Optional");
    } // -- end main
    
    public void testNPE() {
    	String vardas = null;
    	try {
    		Optional optVardas = Optional.of(vardas);
    	} catch (Exception e) {
    		System.out.println("NPE, kai Optional.of(null)");
    	}
    }
    
    public int getListLength(Optional<List<String>> pList) {
    	return pList.map(List::size).orElse(0);
    }
    
    public static Optional<Integer> stringToInt (final String pStr) {
    	try {
    		return Optional.of(Integer.parseInt(pStr));
    	} catch ( NumberFormatException e ) { 
    		return Optional.empty();
    	}
    }
    
    static String getCarInsurance(Optional<Person> person) {
    	    return person.flatMap(Person::getCar)
    	            .flatMap(Car::getInsurance)
    	            .map(Insurance::getName)
    	            .orElse ( coUnsetText );
    }
    
    public String getCarInsuranceOnAge(Optional<Person> person, Optional<Integer> minAge) {
        return person.filter(p -> p.getAge() >= minAge.orElse(0))
                     .flatMap(Person::getCar)
                     .flatMap(Car::getInsurance)
                     .map(Insurance::getName)
                     .orElse( coUnsetText );
    }
    
    // Atrankos pvz.
    public Insurance pigiausiasDraudikas(Person person, Car car) {
    	Insurance piguva = null;
    	// veiksmai
    	return piguva;
    }
    
    public Optional<Insurance> pigiausiasDraudikasSaugiai(Optional<Person> person, Optional<Car> car) {
        if (person.isPresent() && car.isPresent()) 
        	return Optional.of(pigiausiasDraudikas(person.get(), car.get()));
        
        return Optional.empty();
    }
    
    public Optional<Insurance> pigiausiasDraudikasLambda(Optional<Person> person, Optional<Car> car) {
    	return person.flatMap(p -> car.map(c -> pigiausiasDraudikas(p, c)));
    }
    
    // --------------------------------------------------------------------
    private class Person {	
    	private Car car = null;
    	private Optional<Car> optCar = Optional.empty();
    	private int age = 0;
    	
    	public Optional<Car> getCar() { return optCar; }
    	public Optional<Car> getCarOptionally() { return Optional.ofNullable(car); }
    	public void setCar(Car pCar) {
    		car = pCar;
    		optCar = Optional.ofNullable(car);
    	}
    	
    	public int getAge() {
    		return age;
    	}
    }
    	
    private class Car {
        private Optional<Insurance> insurance;
        public Optional<Insurance> getInsurance() { return insurance; }
    }
        
    private class Insurance {
        private String name;
        public String getName() { return name; }
        public void setName(String pName) {  name = pName; }
    }
    
    
    private class Savybes {
    	Properties props = new Properties();
    	
    	public void add (String pName, String pValue) {
    		props.setProperty(pName, pValue);
    	}
    	
    	public int kiekTrunka_Senobinis(String pName) {
    		String value = props.getProperty(pName);
    		if (value != null) {
    			try {
    				int i = Integer.parseInt(value);
    				if (i > 0) return i;
    			} catch ( NumberFormatException e) {}
    		}
    		return 0;
    	}
    	
    	public int kiekTrunka (String pName) {		
    		return Optional.ofNullable(props.getProperty(pName))
                    .flatMap(LabasOptional::stringToInt)
                    .filter(i -> i > 0)
                    .orElse(0);		
    	}
    }
    
    }

    Vykdymo rezultatai:

    Labas, Optional
    
    Nenustatyta
    ok
    Gavom: Optional.empty
    Sveikasis: Optional.empty
    vienas trunka: 1
    true trunka: 0
    minustrys trunka: 0
    keturi trunka: 0
    penki trunka: 5
    
    Papildomai:
    NPE, kai Optional.of(null)
    NPE, kai kviesta su Optional parametru lygiu null
    Kompanijos: 4
    Kiekis kai empty Optional: 0
    
    Sudie, Optional

    (c) 2016, 2019. Vartiklis. Visos teisės saugomos. Leidžiama naudoti tik asmeninės savišvietos tikslais. Bet koks platinimas bet kokiomis priemonemis, viso teksto arba atskiros jo dalies, draudžiamas!

    Ka-ka-rie-kūūūū...
    Tiesiog - Java
    'Java' ir ne tik ji!
    Skriptai - ateities kalbos?
    JavaScript pradžiamokslis
    Anotacijos Java kalboje
    Lyginant su gimtąja kalba
    Programavimas Unix aplinkoje
    Programavimo kalbų klegesys
    Pitonas, kandantis sau uodegą!
    AWK kalba - sena ir nuolat aktuali
    Pirmasis „Java“ įskiepis Lietuvoje
    Lambda išraiškos – Java į naują lygį
    CGI.pm biblioteka: sausainiai
    Vaizdi rašysena - VB Script
    Programavimo kalbų istorija
    Dygios JavaScript eilutės
    Iš kur Javos tas lėtumas?
    Unix komandinė eilutė
    Ruby on Rails
    AdvancedHTML
    Vartiklis