83
Haladó Java programozás

Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

  • Upload
    others

  • View
    10

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Haladó Java programozás

Page 2: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Haladó Java programozás

• Java 5 • Generikusok • (Enumok)

• Java 7 • Sztringek a switchben • try-with-resources

• Java 8 • Alapértelmezett interfészmetódusok • Lambda-kifejezések • Funkcionális interfészek • Streamek • Egyebek

Page 3: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Generikusok a Java nyelvben Bővebb információ:

http://javarevisited.blogspot.sg/2011/09/generics-java-example-tutorial.html

http://docs.oracle.com/javase/tutorial/java/generics/

http://thegreyblog.blogspot.hu/2011/03/java-generics-tutorial-part-i-basics.html

Page 4: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Generikusok

• Célja: fordítási idejű típusbiztonság • Futásidejű ClassCastException-ök megelőzése

• Típuserózió (type erasure): • A típusinformáció csak fordítási időben érhető el, a bájtkódba nem kerül be

• Értelemszerűen futásidőben sem érhető el • Pl.: List<String> esetén a bájtkódba már csak a List nyers típus (raw type)

kerül • Szükség esetén típuskényszerítést is belefordít a kódba

• Típuskövetkeztetés/típuslevezetés (type inference, Java 7 óta): • A Java fordító azon képessége, hogy a metódushívások és a hozzájuk tartozó

deklarációk alapján meghatározza az(oka)t a típusparaméter(eke)t, amelyek lehetővé teszik a hívást

• A legspecifikusabb ilyen típust határozza meg

Page 5: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Jelölésrendszer, elnevezési konvenciók Generikus fogalom Jelentés

Set<E> Generikus típus, E a formális típusparameter

Set<Integer> Parametrizált típus, Integer az aktuális típusparaméter

<T extends Comparable> Korlátozott típusparaméter (bounded type parameter)

<T super Comparable> Korlátozott típusparaméter (bounded type parameter)

Set<?> Korlátozatlan típushelyettesítő (unbounded wildcard)

<? extends T> Korlátozott típushelyettesítő (bounded wildcard type)

<? super T> Korlátozott típushelyettesítő (bounded wildcard type)

Set Nyers típus (raw type)

<T extends Comparable<T>> Rekurzív típuskorlát (recursive type bound)

T – típus E – elem K – kulcs V - érték N – szám

Page 6: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Típushelyettesítők (wildcards)

• Korlátozott (bounded wildcard): • <? extends T>

• A típushelyettesítők kovariánsak a felső korlátjukra nézve

• <? super T> • A típushelyettesítők kontravariánsak az alsó korlátjukra nézve

• Korlátozatlan (unbounded wildcard): • <?>

• Ekvivalens a <? extends Object>-tel

Page 7: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

• A Set<T> parametrizált típus a Set nyers típus altípusa: Set setNyersTípusú = new HashSet<String>();

setNyersTípusú = new HashSet<Integer>();

• A Set<Object> bármilyen elemet tartalmazhat: Set<Object> setTetszőlegesTípusú = new HashSet<Object>();

setTetszőlegesTípusú.add("abc"); //legális

setTetszőlegesTípusú.add(new Float(3.0f)); //legális

setTetszőlegesTípusú = new HashSet<Integer>(); //illegális

• Set<?> ismeretlen típust jelöl, az alábbiak legálisak: Set<?> setIsmeretlenTípusú = new LinkedHashSet<String>();

setIsmeretlenTípusú = new LinkedHashSet<Integer>();

Page 8: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

• A parametrizált típusok a típusok szintjén követik az öröklődési szabályokat:

Set<String> setOfString = new HashSet<String>(); //legális

setOfString = new LinkedHashSet<String>(); //legális

De ez nem igaz a típusparaméterekre! Pl. Set<Object>-nek nem adhatunk Set<String>-et!

• A Set<? extends Number> bármilyen elemet tartalmazhat:

Set<? extends Number> setTetszőlegesSzámTípusú = new

HashSet<Integer>(); //legális, mert Integer extends Number

setTetszőlegesSzámTípusú = new HashSet<Float>(); //legális

• Osztályliterálban nem használhatunk generikusokat

List.class //legális

List<Integer>.class //fordítási hiba

Page 9: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

• A Set<? super TreeMap> TreeMap objektumokat, vagy a TreeMap bármely ősének objektumait tartalmazhatja:

Set<? super TreeMap> setTreeMapSzupertípusú = new LinkedHashSet<TreeMap>(); //legális

setTreeMapSzupertípusú = new HashSet<SortedMap>(); //legális, mivel SortedMap a TreeMap szuperosztálya

setTreeMapSzupertípusú = new LinkedHashSet<Map>(); //legális, mivel Map a TreeMap szupertípusa

• Generikus metódusok írásakor a típusparaméter deklarációjának helye a metódus szignatúrájában a módosítók és a visszatérési típus között van, pl.:

public static <T> T identical(T source){

return source;

}

Page 10: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Fogalmak

Egy programozási nyelv típusrendszerében egy szabály vagy típuskonstruktor

• kovariáns, ha megőrzi a típusok specifikusabbtól általánosabb felé történő rendezettségét;

• kontravariáns, ha megfordítja a sorrendet;

• invariáns, ha a fentiek egyike sem igaz.

Page 12: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Kovariancia • Javában a tömbök kovariánsak

• Egy T[] elemei T-be, vagy T valamely altípusába tartozóak lehetnek

Number[] numbers = new Number[3];

numbers[0] = new Integer(10);

numbers[1] = new Double(3.14);

numbers[2] = new Byte(0);

• S[] altípusa T[]-nek, ha S altípusa T-nek, vagyis legális a következő:

Integer[] myInts = {1,2,3,4};

Number[] myNumber = myInts;

• Mi történik?

myNumber[0] = 3.14; //ArrayStoreException nem ellenőrzött kivétel

Page 13: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Kovariancia List<? extends Number> covariantList = new ArrayList<Integer>();

• Olyan elemek listája, amelyek típusa Number vagy annak valamely leszármazottja

• Olvashatunk • mivel bármi, ami a listában van, felkényszeríthető Number-ré

Number n = covariantList.get(0); //legális

• Az alábbi fordítási hibát ad:

Integer i = covariantList.get(0); //fordítási hiba

• De nem írhatunk! • mert a fordító nem tudja, hogy milyen tényleges objektum szerepel az általános szerkezetben

• ez a Number bármely leszármazottja – pl. Integer, Double, Long – lehet

covariantList.add(45L); //fordítási hiba

covariantList.add(123); //szintén

Page 14: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Kontravariancia List<? super Number> contravariantList = new ArrayList<Object>();

• Olyan elemek listája, amelyek típusa Number vagy valamely őse

• Írhatunk • az általánosabb struktúrába bármit tehetünk contravariantList.add(100); //legális

contravariantList.add(43L); //legális

contravariantList.add(3.14); //legális

contravariantList.add(o); //fordítási hiba

• De nem olvashatunk bármit! • Biztonságosan csak Object-et! Integer i = contravariantList.get(0); //fordítási hiba

Number n = contravariantList.get(0); //fordítási hiba

Object o = contravariantList.get(0); //legális

Page 15: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Típushelyettesítési „kocka”

"Java wildcard subtyping" by Vilhelm.s - Own work. Licensed under CC BY-SA 3.0 via Wikimedia Commons - http://commons.wikimedia.org/wiki/File:Java_wildcard_subtyping.svg#mediaviewer/File:Java_wildcard_subtyping.svg

Page 16: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

PECS: Producer extends, consumer super

• Másnéven get and put principle

• Használjunk extends típushelyettesítőt, ha csak kiveszünk értékeket egy struktúrából

• Használjunk super típushelyettesítőt, ha csak beteszünk értékeket egy struktúrába

• Ne használjunk típushelyettesítőt, ha mindkettőt végezzük

• Példa: T típusú elemek kollekciója esetén (Collection<T>) • Ha végigmegyünk egy kollekción, és valamit akarunk tenni az elemeivel

• Ekkor a kollekció a termelő (producer), ezért célszerű Collection<? extends T>-t használni, hiszen így T bármely altípusának objektumát is beletehetjük

• Ha pakolni szeretnénk a kollekcióba • Ekkor a kollekció a fogyasztó (consumer), ezért célszerű Collection<? super T>-t

használni, hiszen nem érdekel, mi van a kollekcióban, mindaddig, amíg T típusú elemeket hozzáadhatok

Page 17: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Példa extends típushelyettesítő használatára

public static double sum(Collection<? extends Number> nums) {

double s = 0.0;

for (Number num : nums)

s += num.doubleValue();

return s;

}

List<Integer> ints = Arrays.asList(1,2,3); //sum(ints) == 6.0;

List<Double> doubles = Arrays.asList(2.78,3.14); //sum(doubles) == 5.92;

List<Number> nums = Arrays.<Number>asList(1,2,2.78,3.14); //sum(nums) == 8.92;

Page 18: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Példa super típushelyettesítő használatára

public static void count(Collection<? super Integer> ints, int n) {

for (int i = 0; i < n; i++)

ints.add(i);

}

List<Integer> ints = new ArrayList<Integer>();

count(ints, 5); //[0, 1, 2, 3, 4]

List<Number> nums = new ArrayList<Number>();

count(nums, 5); //[0, 1, 2, 3, 4]

nums.add(5.0); //[0, 1, 2, 3, 4, 5.0]

List<Object> objs = new ArrayList<Object>();

count(objs, 5); //[0, 1, 2, 3, 4]

objs.add("öt"); //[0, 1, 2, 3, 4, öt]

Page 19: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Mikor ne használjuk egyiket sem?

public static double sumCount(Collection<Number> nums, int n) {

count(nums, n);

return sum(nums);

}

Page 20: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Legjobb gyakorlatok generikusok használatához

• Kerüljük a nyerstípusokat! • Használjunk generikus típusokat, parametrizált osztályokat és metódusokat!

• Részesítsük előnyben a kollekcióosztályokat a tömbökkel szemben!

• Használjunk korlátozott típusparamétereket! • Így tudunk rugalmas API-t készíteni

• A @SuppressWarnings("unchecked") annotációt a lehető legszűkebb körben használjuk (ha egyáltalán)!

• Pl. egy egész metódus annotálása helyett annotáljunk csupán egy sort

• Konvertáljuk régi kódjaink nyerstípusú osztályait generikussá! • Robusztusabbá válik a kódunk

Page 21: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Sztringek a switch-ben Bővebb információ:

http://www.theserverside.com/tutorial/The-Switch-to-Java-7-Whats-New-with-Conditional-Switches

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html

Page 22: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

public static String getNapTípusa(String hétNapja) { String napTípusa; if (hétNapja == null) throw new IllegalArgumentException("Érvénytelen nap: " + hétNapja); switch (hétNapja) { case "Hétfő": napTípusa = "Munkahét eleje"; break; case "Kedd": case "Szerda": case "Csütörtök": napTípusa = "Hét közepe"; break; case "Péntek": napTípusa = "Munkahét vége"; break; case "Szombat": case "Vasárnap": napTípusa = "Hétvége"; break; default: throw new IllegalArgumentException("Érvénytelen nap: " + hétNapja); } return napTípusa; }

Page 23: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Sztringek a switch-ben

• Könnyebben olvasható, mint az egymásba ágyazott if–else–if-ek • A generált bájtkód is hatékonyabb

• A sztringek természetesen érzékenyek a kis- és nagybetűk közötti különbségre!

• Az értékvizsgálat az equals metódus segítségével történik • Biztosítsuk, hogy ne keletkezzen NullPointerException!

• A case címkék konstans kifejezések lehetnek

• A szelektor statikus típusa String kell, hogy legyen • Nem elég dinamikusan annak lenni!

Page 25: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Erőforráskezelés régen…

private static void printFile() throws IOException { InputStream input = null; try { input = new FileInputStream("file.txt"); int data = input.read(); while (data != -1) { System.out.print((char) data); data = input.read(); } } finally { if (input != null) input.close(); } }

Hol következhet be kivétel?

Ezt külön try-ba kellene tenni

Page 26: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

…és most (Java 7 óta)

private static void printFileJava7() throws IOException { try (FileInputStream input = new FileInputStream("file.txt")) { int data = input.read(); while (data != -1) { System.out.print((char) data); data = input.read(); } } }

Page 27: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

try–with–resources

• Biztosítja, hogy az utasításban deklarált erőforrások lezárása megtörténik

• Ehhez az erőforrásnak implementálnia kell a java.lang.AutoCloseable interfészt

• Több erőforrás is kezelhető:

private static void printFileJava7() throws IOException { try (FileInputStream input = new FileInputStream("file.txt"); BufferedInputStream bufferedInput = new BufferedInputStream(input)) { int data = bufferedInput.read(); while (data != -1) { System.out.print((char) data); data = bufferedInput.read(); } } }

Page 28: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Alapértelmezett interfészmetódusok

Page 29: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Problémák interfészekkel

• Létező interfészek evolúciója • Mi történik, ha egy interfészt egy új metódussal kellene bővíteni?

• Pl. a kollekcióinterfészeket kiegészítenék egy-egy stream(), parallelStream() ill. forEach() metódussal

• Minden olyan osztályban, amely implementálja az interfészt, implementálnunk kell a metódust! • Ha nem tesszük, osztályaink le sem fordulnak

• Különösen problémás 3rd party alkalmazások esetén

• Nem elég rugalmas tervezés • Konkrét osztályok közötti funkcionalitás-megosztás absztrakt osztályokkal lehetséges

• Az egyszeres öröklődés korlátokat szabott

• Számos esetben ún. adapter osztályt kellett létrehozni azért, hogy ne kelljen minden osztályban az interfész összes metódusát implementálni, példák (részletesebben ld. később):

• SAX-feldolgozás: ContentHandler stb. interfészek vs. DefaultHandler osztály

• Swing: MouseListener stb. interfészek vs. MouseAdapter osztály

• Ezen osztályok nagy technikai segítséget adtak, de nehezebben olvashatóvá tették a kódot azáltal, hogy elrejtették, hogy mely interfészek megvalósítása történik

Page 30: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Megoldás: alapértelmezett metódusok!

• Alternatív nevek: defender methods, virtual extension methods

• Alapértelmezett metódus nem lehet olyan szignatúrájú, amely az Object osztály valamely nem final módosítójú metódusáéval megegyezik

• Fordítási hiba jár érte, ha megpróbáljuk

• Az alapértelmezett metódust az interfészben adjuk meg, de az implementáló osztályok felüldefiniálhatják

• Nincs probléma az implementáló osztályokban evolúció esetén • Nincs szükség felesleges osztályokra

• Bináris kompatibilitás a régi interfésztől függő osztályokkal

• Szükséges elemek: • default kulcsszó • Alapértelmezett implementációt tartalmazó blokk

• Hátrány: az interfész szennyeződik oda nem illő dolgokkal (implementáció)

Page 31: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Példa

public interface Addressable { String getStreet(); String getCity(); default String getFullAddress() { return getStreet() + ", " + getCity(); } }

public class Letter implements Addressable { private String street, city; public Letter(String street, String city) { this.street = street; this.city = city; } @Override public String getCity() { return city; } @Override public String getStreet() { return street; } @Override public String getFullAddress() { return city() + ", " + street; } public static void main(String[] args) { Letter l = new Letter("123 AnyStreet", "AnyCity"); System.out.println(l.getFullAddress()); } }

123 AnyStreet ,

AnyCity

Page 32: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Lehetőségek alapértelmezett metódussal rendelkező interfészből származtatáskor • Nem teszünk említést az alapértelmezett metódusról

• Ekkor a leszármazott örökli azt

• Újradeklaráljuk az alapértelmezett metódust • Ez abstract-tá teszi

• Felüldefiniáljuk az alapértelmezett metódust • Vagyis új törzset rendelünk hozzá

Page 33: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Lambda-kifejezések Bővebb információ:

http://javarevisited.blogspot.hu/2014/02/10-example-of-lambda-expressions-in-java8.html

http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

http://www.slideshare.net/langer4711/lambdas-funct-progjdays2013

http://www.slideshare.net/martyhall/lambda-expressions-and-streams-in-java-8-presented-by-marty-hall-to-google-inc

http://www.drdobbs.com/jvm/lambda-expressions-in-java-8/240166764

Page 34: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Előzmények – Beágyazott osztály (nested class)

• Statikus beágyazott osztály (static nested class)

• Belső osztály (inner class) • Lokális osztály (local class)

• Névtelen osztály (anonymous class)

Page 35: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Beágyazott osztály

• Egy osztályon belül deklarált másik osztály • Mind a négyféle láthatóság jelen lehet (public, protected, csomag szintű, private)

• Külső szinten szereplő osztályok csak public vagy csomag szintű láthatósággal bírhatnak!

• Befoglaló/tartalmazó osztályának (enclosing class) tagja

• Statikus • Terminológia: statikus beágyazott osztály (static nested class) • Nem férnek hozzá befoglaló osztályuk példány szintű tagjaihoz!

• Nem statikus • Terminológia: belső osztály (inner class) • A befoglaló osztály példány szintű tagjait még akkor is eléri, ha azok private

láthatóságúak!

Page 36: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Beágyazott osztályok haszna

• Logikailag csoportosíthatjuk a csak egyetlen helyen használt osztályokat • Ha egy osztály csak egyetlen más osztály számára hasznos, akkor logikus, hogy

együtt tartsuk a kettőt.

• Növeli a bezárást • A és B legfelső szintű osztályok esetén, ha B-nek el kell érnie A-beli tagokat,

akkor A-ban ezek nem lehetnek privátok. Ha B-t beágyazzuk A-ba, akkor A tagjai úgy lehetnek privátak, hogy B eléri őket. Ráadásul B-t is elrejthetjük a világ elől.

• A kód könnyebben olvasható és karbantartható lesz

Page 37: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Lambda-kifejezések

• Egy funkcionális interfészt megvalósító névtelen osztály egy objektumát adhatjuk meg

new ValamilyenInterfész() {

@Override

public ValamilyenTípus valamilyenMetódus(paraméterek) {

törzs

}

}

Helyette ezt írjuk:

(paraméterek) -> {törzs}

Page 38: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

{

return p.getGender() == Person.Sex.MALE

&& p.getAge() >= 18

&& p.getAge() <= 25;

}

Lambda-kifejezések

• Kerek zárójelek között megadott, egymástól vesszővel elválasztott formális paraméterek

• A paraméter típusa elmaradhat • Ha csak egy paraméter van, akkor a zárójelpár is elmaradhat • Ha egy sincs, akkor viszont kötelező kitenni az üres zárójelpárt!

• A nyíl token

• Egy törzs, amely egyetlen kifejezést vagy egy blokkot tartalmaz • Kifejezés megadása esetén az kiértékelődik • Használhatunk return utasítást is

• Mivel ez nem kifejezés, ezért blokkba helyezzük

• A void metódusok hívását nem kötelező blokkba tenni, az alábbi szabályos: email -> System.out.println(email)

p -> p.getGender() == Person.Sex.MALE

&& p.getAge() >= 18

&& p.getAge() <= 25

Page 39: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Példa több formális paraméterű lambda-kifejezésre public class Calculator {

interface IntegerMath {

int operation(int a, int b);

}

public int operateBinary(int a, int b, IntegerMath op) {

return op.operation(a, b);

}

public static void main(String... args) {

Calculator myApp = new Calculator();

IntegerMath addition = (a, b) -> a + b;

IntegerMath subtraction = (a, b) -> a - b;

System.out.println("40 + 2 = " + myApp.operateBinary(40, 2, addition));

System.out.println("20 - 10 = " + myApp.operateBinary(20, 10, subtraction));

}

}

Page 40: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Láthatóság

• Láthatóság szempontjából a lambda-kifejezések nem vezetnek be újabb szintet („lexically scoped”)

• Épp ezért nincs lyuk sem a hatáskörben

• A benne lévő esetleges deklarációkat is úgy értjük, mintha azok a befoglaló környezetben lennének

• Nem változtathatják meg egy lokális változó értékét (de egy példányváltozójét igen!)

• final vagy effektív final

Page 42: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Funkcionális interfész

• Másnéven: SAM (Single Abstract Method) interfész

• Minden lambda megfelel valamely típusnak, amit egy interfész ad meg

• Egy funkcionális interfész pontosan egy absztraktmetódus-deklarációt tartalmaz

• Mindaddig, amíg ezt biztosítjuk, tetszőleges interfészeket használhatunk lambda-kifejezésként

• Ennek biztosítására vezették be a @FunctionalInterface annotációtípust

• Ennek hatására a fordító fordítási hibát dob, ha egytől eltérő számú absztraktmetódus-deklaráció

Page 43: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Példa

@FunctionalInterface public interface Converter<F, T> { T convert(F from); }

Converter<String, Integer> converter = (from) -> Integer.valueOf(from); Integer converted = converter.convert("123"); System.out.println(converted); // 123

Page 44: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Metódus- és konstruktorreferenciák

• Anélkül hivatkozhatunk egy metódusra, hogy ténylegesen meghívnánk • A példányosítás és a tömblétrehozás is ide tartozik

• ::

• Példák:

String::length // példánymetódus

System::currentTimeMillis // statikus metódus

List<String>::size // explicit generikustípus-paraméter

List::size // kikövetkeztetett generikustípus-paraméter

System.out::println

"abc"::length

super::toString

ArrayList::new

int[]::new

Page 45: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Példa metódusreferenciára public class Something { public String startsWith(String s) { return String.valueOf(s.charAt(0)); } public static void main(String[] args) { Something something = new Something(); Converter<String, String> converter = something::startsWith; String converted = converter.convert("Java"); System.out.println(converted); // "J" } }

Page 46: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Példa konstruktorreferenciára

• Tekintsük az alábbi osztályt, két konstruktorral: class Person { String firstName, lastName; Person() {} Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } }

• Készítsünk egy factory interfészt: interface PersonFactory<P extends Person> { P create(String firstName, String lastName); }

• A fordító a create szignatúrája alapján automatikusan kiválasztja a megfelelő konstruktort: PersonFactory<Person> personFactory = Person::new; Person person = personFactory.create("Peter", "Parker");

Page 47: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Beépített funkcionális interfészek

• Számos régi interfész is megkapta a @FunctionalInterface annotációt, pl. Runnable, Comparator

• Újak is megjelentek (java.util.function csomag): • Predicate

• Function

• Supplier

• Consumer

Page 48: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Predicate • A predikátumok egyparaméteres logikai függvények

• Funkcionális metódus: test(…) – kiértékeli a predikátumot az adott paraméterrel

• Alapértelmezett metódusai: and(…), or(…), negate()

Predicate<String> predicate = (s) -> s.length() > 0;

predicate.test("foo"); // true

predicate.negate().test("foo"); // false

Predicate<Boolean> nonNull = Objects::nonNull;

Predicate<Boolean> isNull = Objects::isNull;

Predicate<String> isEmpty = String::isEmpty;

Predicate<String> isNotEmpty = isEmpty.negate();

Page 49: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Function

• Egyparaméteres függvények, amelyek meghatároznak egy értéket

• Funkcionális metódus: apply(…) – alkalmazza a függvényt a paraméterére

• Alapértelmezett metódusai: • compose(before): előbb a paramétereként kapott (before) függvényt, majd ezt

alkalmazza

• andThen(after): előbb ezt a függvényt, majd a paramétereként kapottat (after) alkalmazza

Function<String, Integer> toInteger = Integer::valueOf;

Function<String, String> backToString =

toInteger.andThen(String::valueOf);

backToString.apply("123"); // "123"

Page 50: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Supplier

• Egy valamilyen eredményeket biztosító (szolgáltató) objektumot ír le

• Funkcionális metódus: get() – megadja az eredményt

Supplier<Person> personSupplier = Person::new;

personSupplier.get(); // new Person

Page 51: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Consumer

• Egyparaméteres, eredmény nélküli művelet

• A többi funkcionális interfésztől eltérően mellékhatással rendelkezik

• Funkcionális metódus: accept(…) – végrehajtja a műveletet paraméterére

• Alapértelmezett metódus: • andThen(after): egy összetett Consumer-t határoz meg, amelyet úgy kapunk,

hogy ezen Consumer után végrehajtjuk after-t is

Consumer<Person> greeter = (p) -> System.out.println("Hello, " +

p.firstName);

greeter.accept(new Person("Luke", "Skywalker"));

Page 52: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Streamek Bővebb információ:

http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/

http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html

http://www.slideshare.net/martyhall/lambda-expressions-and-streams-in-java-8-presented-by-marty-hall-to-google-inc

http://java67.blogspot.hu/2014/04/java-8-stream-api-examples-filter-map.html

Page 53: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Mik a streamek?

• A streamek monádok • A funkcionális programozásban monád alatt egy olyan struktúrát értünk, amely

lépéssorozatként megadott számításokat reprezentál. Egy monád szerkezetű típus definiálja, hogy mit jelent a típus műveleteinek láncolása, vagy függvényeinek beágyazása.

• Egy stream egy elemsorozatot reprezentál, és különféle műveleteket biztosít az elemeken végzett számítások érdekében

• Nincs mögöttük tárterület, vagyis nem kollekciók!

• A műveletek lehetnek köztesek (intermediate) vagy terminálisak (terminal) • Fluent API: a stream műveletek streameket adnak vissza

• Legtöbbjük egy lambda-kifejezést kaphat paraméterül • Ez adja meg a művelet pontos viselkedését

Page 54: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Köztes műveletek • Streamet adnak vissza, így pontosvessző nélkül láncolhatjuk egymáshz a

köztes műveleteket

• Példa köztes műveletekre: • filter: egy szűrő alkalmazásával új streamet hoz létre a régi alapján

• map: egy új streamet hoz létre a régi elemeinek leképezése alapján

• sorted: létrehoz egy új streamet a régi elemeinek rendezésével

• Teljes lista: http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

• Csővezetéket alkotnak (operation pipeline) List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1"); myList.stream() .filter(s -> s.startsWith("c")) .map(String::toUpperCase) .sorted() .forEach(System.out::println);

Page 55: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Terminális műveletek

• Vagy void, vagy nem streamet visszaadó

• Pl.: forEach vagy reduce

List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1"); myList.stream() .filter(s -> s.startsWith("c")) .map(String::toUpperCase) .sorted() .forEach(System.out::println); Eredmény: C1 C2

Page 56: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Műveletek kívánatos jellemzői

• Mellékhatásmentesség (non-interfering operation): nem módosítja a stream adatforrását

• Pl.: egyetlen lambda-kifejezés sem változtatja meg myList-et

• Állapotmentesség (stateless operation): a műveletvégrehajtás determinisztikus

• Pl.: egyetlen lambda-kifejezés sem függ olyan változótól (állapottól), amely a végrehajtás során megváltozhat

Page 57: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Streamtípusok

• A streameket különféle adatforrások (különösen kollekciók) alapján hozhatjuk létre

• A java.util.Collection új metódusai: • stream(): szekvenciális stream létrehozására

• parallelStream(): párhuzamos (több szálon futni képes) stream létrehozására

Page 58: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Szekvenciális streamek létrehozása

• stream() • Objektumok sima streamjét adja vissza

• of() • Objektumreferenciákból állít elő streamet

• range() • Primitív típusok streamjeinek (IntStream, LongStream, FloatStream, …)

inicializálására

Stream.of("a1", "a2", "a3") .findFirst() .ifPresent(System.out::println); // a1

IntStream.range(1, 4) .forEach(System.out::println); // 1 // 2 // 3

Page 59: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Primitív streamek

• Hasonlóak a hagyományoshoz, néhány különbséggel: • Specializált lambda-kifejezéseket használnak

• Pl. IntFunction-t Function helyett, IntPredicate-et Predicate helyett, stb.

• További terminális aggregáló műveletei vannak • sum() és average()

• Konverzió oda-vissza van (mapToInt, mapToLong, mapToDouble)

Arrays.stream(new int[] {1, 2, 3}) .map(n -> 2 * n + 1) .average() .ifPresent(System.out::println); // 5.0

Stream.of("a1", "a2", "a3") .map(s -> s.substring(1)) .mapToInt(Integer::parseInt) .max() .ifPresent(System.out::println); // 3

IntStream.range(1, 4) .mapToObj(i -> "a" + i) .forEach(System.out::println); // a1 // a2 // a3

Stream.of(1.0, 2.0, 3.0) .mapToInt(Double::intValue) .mapToObj(i -> "a" + i) .forEach(System.out::println); // a1 // a2 // a3

Page 60: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> { System.out.println("filter: " + s); return true; }) .forEach(s -> System.out.println("forEach: " + s));

Feldolgozási sorrend

• A köztes műveletek lusta kiértékelésűek, vagyis csak akkor hajtódnak végre, ha van terminális művelet

• Példa, ami nem csinál semmit:

• Kiegészítve egy terminálissal:

• Eredmény:

Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> { System.out.println("filter: " + s); return true; });

filter: d2 forEach: d2 filter: a2 forEach: a2 filter: b1 forEach: b1 filter: b3 forEach: b3 filter: c forEach: c

A végrehajtás nem horizontálisan, hanem vertikálisan történik!

Page 61: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

A végrehajtás nem horizontálisan, hanem vertikálisan történik! • Ez csökkentheti az elemeken végrehajtandó műveletek számát

Stream.of("d2", "a2", "b1", "b3", "c") .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .anyMatch(s -> { System.out.println("anyMatch: " + s); return s.startsWith("A"); }); // map: d2 // anyMatch: D2 // map: a2 // anyMatch: A2

Az anyMatch igazat ad vissza, mihelyst a predikátuma alkalmazható a megadott input elemre (2. elem). A vertikális végrehajtás miatt a map csak kétszer kerül hívásra.

Page 62: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

A sorrend számít? Stream.of("d2", "a2", "b1", "b3", "c") .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .filter(s -> { System.out.println("filter: " + s); return s.startsWith("A"); }) .forEach(s -> System.out.println("forEach: "+s));

// map: d2 // filter: D2 // map: a2 // filter: A2 // forEach: A2 // map: b1 // filter: B1 // map: b3 // filter: B3 // map: c // filter: C

Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> { System.out.println("filter: " + s); return s.startsWith("a"); }) .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .forEach(s -> System.out.println("forEach: " + s));

// filter: d2 // filter: a2 // map: a2 // forEach: A2 // filter: b1 // filter: b3 // filter: c

A sorrend számít!

Page 63: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

sorted

• Állapotőrző (stateful) köztes művelet • Ahhoz, hogy rendezzen egy elemsorozatot, nyilván kell tartania az állapotot Stream.of("d2", "a2", "b1", "b3", "c") .sorted((s1, s2) -> { System.out.printf("sort: %s; %s\n", s1, s2); return s1.compareTo(s2); }) .filter(s -> { System.out.println("filter: " + s); return s.startsWith("a"); }) .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .forEach(s -> System.out.println("forEach: " + s));

// sort: a2; d2 // sort: b1; a2 // sort: b1; d2 // sort: b1; a2 // sort: b3; b1 // sort: b3; d2 // sort: c; b3 // sort: c; d2 // filter: a2 // map: a2 // forEach: A2 // filter: b1 // filter: b3 // filter: c // filter: d2

Itt a sorted horizontálisan (a teljes inputra) végrehajtódik!

Page 64: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Javított változat

Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> { System.out.println("filter: " + s); return s.startsWith("a"); }) .sorted((s1, s2) -> { System.out.printf("sort: %s; %s\n", s1, s2); return s1.compareTo(s2); }) .map(s -> { System.out.println("map: " + s); return s.toUpperCase(); }) .forEach(s -> System.out.println("forEach: " + s));

// filter: d2 // filter: a2 // filter: b1 // filter: b3 // filter: c // map: a2 // forEach: A2

Itt a sorted sosem hívódik meg, mert a filter egyeleműre szűkít!

Page 65: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Streamek újrafelhasználása

• Alaphelyzetben nem lehetséges • Egy terminális művelet meghívásakor a stream bezárul

• Megoldás: minden végrehajtani kívánt terminális művelethez hozzunk létre egy új streamláncot

• Minden get() hívás létrehoz egy új streamet

Stream<String> stream = Stream.of("d2", "a2", "b1", "b3", "c") .filter(s -> s.startsWith("a")); stream.anyMatch(s -> true); // ok stream.noneMatch(s -> true); // kivétel (IllegalStateException)

Supplier<Stream<String>> streamSupplier = () -> Stream.of("d2", "a2", "b1", "b3", "c").filter(s -> s.startsWith("a")); streamSupplier.get().anyMatch(s -> true); // ok streamSupplier.get().noneMatch(s -> true); // ok

Page 66: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

class Person { String name; int age; Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return name; } } List<Person> persons = Arrays.asList( new Person("Max", 18), new Person("Peter", 23), new Person("Pamela", 23), new Person("David", 12));

Page 67: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

collect

• Terminális művelet, amellyel egy stream elemeit különféle formára hozhatjuk • Pl. List, Set, Map, stb.

• Kap egy Collector-t, amely négy műveletből áll: • Egy supplier

• Egy accumulator

• Egy combiner

• Egy finisher

• Van sok beépített Collector

Page 68: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

collect példák List<Person> filtered = persons .stream() .filter(p -> p.name.startsWith("P")) .collect(Collectors.toList()); System.out.println(filtered); // [Peter, Pamela]

Map<Integer, List<Person>> personsByAge = persons .stream() .collect(Collectors.groupingBy(p -> p.age)); personsByAge .forEach((age, p) -> System.out.format("age %s: %s\n", age, p)); // age 18: [Max] // age 23: [Peter, Pamela] // age 12: [David]

IntSummaryStatistics ageSummary = persons.stream().collect( Collectors.summarizingInt(p -> p.age)); System.out.println(ageSummary); // IntSummaryStatistics{count=4, sum=76, min=12, average=19.000000, max=23}

Page 69: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Saját Collector

• Fűzzük össze az összes személy nevét egy | (pipe) karakterrel Collector<Person, StringJoiner, String> personNameCollector = Collector.of(() -> new StringJoiner(" | "), // supplier (j, p) -> j.add(p.name.toUpperCase()), // accumulator (j1, j2) -> j1.merge(j2), // combiner StringJoiner::toString); // finisher String names = persons.stream().collect(personNameCollector); System.out.println(names); // MAX | PETER | PAMELA | DAVID

Page 70: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

reduce

• Egy stream összes elemét egyetlen elemre redukálja

• Három változata van • Elemek streamjét a stream pontosan egy elemére redukálja

• Pl. ki a legidősebb személy?

• Egy egységelem és egy asszociatív BinaryOperator accumulator alapján redukál

• Egy egységelem, egy BiFunction accumulator és egy BinaryOperator típusú combiner függvény alapján redukál

• Gyakran ennél egyszerűbb explicit módon kombinálni a map-et és a reduce-t

Page 71: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

reduce példák

persons .stream() .reduce((p1, p2) -> p1.age > p2.age ? p1 : p2) .ifPresent(System.out::println); // Pamela

Ki a legidősebb?

Person result = persons .stream() .reduce(new Person("", 0), (p1, p2) -> { p1.age += p2.age; p1.name += p2.name; return p1; }); System.out.format("name=%s; age=%s", result.name, result.age); // name=MaxPeterPamelaDavid; age=76}

Hozzunk létre egy olyan Person objektumot, amely a stream objektumainak neveit és életkorát aggregálja!

Page 72: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

reduce példák Mennyi a személyek összéletkora?

Integer ageSum = persons .stream() .reduce(0, (sum, p) -> sum += p.age, (sum1, sum2) -> sum1 + sum2); System.out.println(ageSum); // 76

Integer ageSum = persons.stream().reduce(0, (sum, p) -> { System.out.format("accumulator: sum=%s; person=%s\n", sum, p); return sum += p.age; }, (sum1, sum2) -> { System.out.format("combiner: sum1=%s; sum2=%s\n", sum1, sum2); return sum1 + sum2; }); // accumulator: sum=0; person=Max // accumulator: sum=18; person=Peter // accumulator: sum=41; person=Pamela // accumulator: sum=64; person=David

• Egy kis debugolás: • Mit látunk?

• A combiner nem futott le!

Page 73: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

peek

System.out.println("összeg: " + IntStream.range(1, 11) .map(i -> i + 5) .peek(i -> System.out.print(i + " ")) .reduce(0, (i, j) -> i + j) ); // 6 7 8 9 10 11 12 13 14 15 összeg: 105

• Köztes művelet

Page 74: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Párhuzamosítsunk! Integer ageSum = persons .parallelStream() .reduce(0, (sum, p) -> { System.out.format("accumulator: sum=%s; person=%s\n", sum, p); return sum += p.age; }, (sum1, sum2) -> { System.out.format("combiner: sum1=%s; sum2=%s\n", sum1, sum2); return sum1 + sum2; }); // accumulator: sum=0; person=Pamela // accumulator: sum=0; person=David // accumulator: sum=0; person=Max // accumulator: sum=0; person=Peter // combiner: sum1=18; sum2=23 // combiner: sum1=23; sum2=12 // combiner: sum1=41; sum2=35

Page 75: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Párhuzamos streamek

• Futásidejű teljesítmény növelése céljából

• Egy ún. ForkJoinPool-t használnak, amely legfeljebb 5 szálra bont

• A következő JVM paraméterrel változtatható: -Djava.util.concurrent.ForkJoinPool.common.parallelism=5

• Kétféleképpen juthatunk hozzá: • A kollekciók parallelStream() metódusával • A parallel() köztes metódussal egy szekvenciális streamet

párhuzamosíthatunk

ForkJoinPool commonPool = ForkJoinPool.commonPool(); System.out.println(commonPool.getParallelism()); // 3

Page 76: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Példa Arrays.asList("a1", "a2", "b1", "c2", "c1") .parallelStream() .filter(s -> { System.out.format("filter: %s [%s]\n", s, Thread.currentThread().getName()); return true; }) .map(s -> { System.out.format("map: %s [%s]\n", s, Thread.currentThread().getName()); return s.toUpperCase(); }) .forEach( s -> System.out.format("forEach: %s [%s]\n", s, Thread.currentThread().getName()));

filter: b1 [main] map: b1 [main] filter: a2 [ForkJoinPool.commonPool-worker-1] filter: a1 [ForkJoinPool.commonPool-worker-2] forEach: B1 [main] filter: c1 [ForkJoinPool.commonPool-worker-3] map: c1 [ForkJoinPool.commonPool-worker-3] forEach: C1 [ForkJoinPool.commonPool-worker-3] filter: c2 [main] map: c2 [main] forEach: C2 [main] map: a1 [ForkJoinPool.commonPool-worker-2] forEach: A1 [ForkJoinPool.commonPool-worker-2] map: a2 [ForkJoinPool.commonPool-worker-1] forEach: A2 [ForkJoinPool.commonPool-worker-1]

Page 77: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Aggregált műveletek és iterátorok

• Az aggregált műveletek (pl. forEach) hasonlónak tűnhetnek az iterátorokhoz

• Van azonban néhány alapvető különbség: • Belső iterációt használnak

• Nincs next-hez hasonló metódusuk

• Belső delegációval az alkalmazásunk mondja meg, melyik kollekción iterálunk, de a JDK mondja meg, hogyan

• Egy stream elemeit dolgozzák fel • Nem pedig közvetlenül a kollekcióét. Épp ezért hívjuk őket stream műveleteknek

• A viselkedést paraméterek formájában adhatjuk meg • A legtöbb aggregált művelet lambda-kifejezéssel paraméterezhető, ezáltal testre szabható

Page 78: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Egyéb újdonságok A teljesség igénye nélkül, természetesen

Page 79: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

java.util.Optional

• Egy olyan konténerobjektum, amely vagy tartalmaz null értéket, vagy nem

• Java 8 óta

• Főbb metódusok: • static empty(): visszaad egy üres Optional példányt

• static of(value): visszaad egy megadott value értékkel rendelkező Optional-t

• static ofNullable(value): ha value nem null, ezt adja vissza Optional-ként, különben pedig egy üres Optional-t

• get(): ha az Optional-ben van érték, azt adja meg, különben NoSuchElementException

• ifPresent(): igazat ad, ha az Optional-ben van érték, különben hamisat

• orElse(other): ha van benne érték, azt adja vissza, különben other-t

Page 80: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Mire jó az Optional?

• Rákényszerítjük a hívót, hogy gondoskodjon arról az esetről, ha egy adott objektum nem létezik

• Ezáltal kevesebb NPE (NullPointerException) következik be

• Minden olyan függvénynek Optional visszatérési típussal kellene rendelkeznie, amelyről elképzelhető, hogy nem ad vissza értéket

Page 81: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

Hogyan használjuk?

public Optional<Student> findStudentByNeptunId(String neptunId) {

}

Optional<Student> optional = findStudentByNeptunId(neptunId);

if (optional.isPresent()) {

Student st = optional.get();

/* használhatjuk a Student objektumot */

}

else {

/* törődjünk azzal az esettel, amikor nem található */

}

Page 82: Haladó Java programozás - unideb.hu Java... · 2018-05-09 · Haladó Java programozás •Java 5 •Generikusok •(Enumok) •Java 7 •Sztringek a switchben •try-with-resources

java.util.StringJoiner

• Egy-egy opcionális prefix és suffix között megadott elhatárolójel segítségével összeállított karaktersorozatot hoz létre

• Collectors.joining

int[] t = new int[] {1,2,3,4,5,6,7,8,9,10}; StringJoiner sj = new StringJoiner(", ", "[", "]"); for (int i : t) sj.add(i + ""); // String.valueOf(i); System.out.println(sj); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

System.out.println(Arrays.stream(t) .mapToObj(i -> i + "") // .mapToObj(String::valueOf) .collect(Collectors.joining(", ", "[", "]"))); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]