Kihívások a Java 8-ban

1. Áttekintés

A Java 8 néhány új funkciót vezetett be, amelyek főleg a lambda kifejezések körül forogtak. Ebben a rövid cikkben néhányuk hátrányait vesszük szemügyre.

És bár ez nem teljes lista, ez a Java 8 új funkcióival kapcsolatos leggyakoribb és legnépszerűbb panaszok szubjektív gyűjteménye.

2. Java 8 adatfolyam és szálkészlet

Először is, a Párhuzamos folyamok célja a szekvenciák egyszerű párhuzamos feldolgozásának lehetővé tétele, és ez nagyon jól működik egyszerű forgatókönyvek esetén.

A Stream az alapértelmezett, common értéket használja ForkJoinPool - szétválasztja a szekvenciákat kisebb darabokra, és több szál segítségével végez műveleteket.

Van azonban fogás. Nincs erre jó módszer melyik ForkJoinPool használni és ezért, ha az egyik szál elakad, akkor az összes többi a megosztott készlet használatával meg kell várnia a régóta futó feladatok befejezését.

Szerencsére van erre megoldás:

ForkJoinPool forkJoinPool = új ForkJoinPool (2); forkJoinPool.submit (() -> / * párhuzamos folyamú csővezeték * /) .get ();

Ez létrehoz egy új, külön ForkJoinPool és a párhuzamos adatfolyam által generált összes feladat a megadott készletet fogja használni, és nem a megosztott, alapértelmezettben.

Érdemes megjegyezni, hogy van még egy lehetséges fogás: „Ez a technika, amikor feladatot küldenek egy villás csatlakozású készlethez, a párhuzamos adatfolyam futtatásához ebben a készletben, megvalósítási„ trükk ”és nem garantáltan működikStuart Marks - Java és OpenJDK fejlesztő szerint az Oracle-től. Fontos árnyalat, amelyet szem előtt kell tartani e technika alkalmazásakor.

3. Csökkent hibakeresés

Az új kódolási stílus mégis egyszerűsíti a forráskódunkatfejfájást okozhat a hibakeresés közben.

Először nézzük meg ezt az egyszerű példát:

public static int getLength (String input) {if (StringUtils.isEmpty (input) {dobja be az új IllegalArgumentException ();} return input.length ();} List hosszai = new ArrayList (); for (String neve: Arrays.asList ( args)) {hosszak.add (getLength (név));}

Ez egy kötelező, kötelező Java kód, amely magától értetődik.

Ha üresen haladunk el Húr inputként - ennek eredményeként - a kód kivételt vet, és a hibakereső konzolon láthatjuk:

itt: LmbdaMain.getLength (LmbdaMain.java:19) itt: LmbdaMain.main (LmbdaMain.java:34)

Most írjuk újra ugyanazt a kódot a Stream API használatával, és nézzük meg, mi történik üresen Húr átadják:

Adatfolyam hosszai = nevek.stream (). Térkép (név -> getLength (név));

A hívásverem a következőképpen fog kinézni:

at LmbdaMain.getLength (LmbdaMain.java:19) at LmbdaMain.lambda $ 0 (LmbdaMain.java:37) at LmbdaMain $$ Lambda $ 1 / 821270929.apply (Ismeretlen forrás) at java.util.stream.ReferencePipeline $ 3 $ 1.accept ( ReferencePipeline.java:193) itt: java.util.Spliterators $ ArraySpliterator.forEachRemaining (Spliterators.java:948) itt: java.util.stream.AbstractPipeline.copyInto (AbstractPipeline.java:512) a java.util.stream.AbstractPipeline.wrapAnd (AbstractPipeline.java:502) a java.util.stream.ReduceOps $ ReduceOp.evaluateSequential (ReduceOps.java:708) címen a java.util.stream.AbstractPipeline.evaluate (AbstractPipeline.java:234) a java.util.stream oldalon. LongPipeline.reduce (LongPipeline.java:438) a java.util.stream.LongPipeline.sum (LongPipeline.java:396) címen a java.util.stream.ReferencePipeline.count (ReferencePipeline.java:526) az LmbdaMain.main (LmbdaMain) oldalon .java: 39)

Ez az az ár, amelyet azért fizetünk, ha a kódban több absztrakciós réteget alkalmazunk. Az IDE-k azonban már szilárd eszközöket fejlesztettek ki a Java adatfolyamok hibakeresésére.

4. Visszatérő módszerek Nulla vagy Választható

Választható a Java 8-ban vezették be, hogy biztosítsák az opció opcióinak típusbiztos módját.

Választható, kifejezetten jelzi, hogy a visszatérési érték nem lehet jelen. Ezért a metódus meghívása visszaadhat egy értéket, és Választható az érték belsejébe csomagolására szolgál - ami hasznosnak bizonyult.

Sajnos a Java visszamenőleges kompatibilitása miatt néha a Java API-k két különböző konvenciót keverünk össze. Ugyanabban az osztályban megtalálhatjuk a nullákat visszatérő módszereket, valamint a visszatérő módszereket Opcionális.

5. Túl sok funkcionális interfész

Ban,-ben java.util.function csomaggal rendelkezünk a lambda kifejezések céltípusainak gyűjteményével. Megkülönböztethetjük és csoportosíthatjuk őket:

  • Fogyasztó - olyan műveletet képvisel, amely néhány argumentumot vesz fel, és nem ad eredményt
  • Funkció - egy olyan függvényt képvisel, amely néhány érvet vesz fel és eredményt produkál
  • Operátor - egy műveletet képvisel bizonyos típusú argumentumokban, és az operandusokkal megegyező típusú eredményt ad vissza
  • Állítmány - predikátumot képvisel (logikai-értékelt függvény) néhány argumentumot
  • Támogató - olyan beszállítót képvisel, amely nem vesz fel érveket és nem ad eredményt

Ezenkívül további típusaink vannak a primitívekkel való munkavégzéshez:

  • IntFogyasztó
  • IntFunction
  • IntPredicate
  • IntSupplier
  • IntToDoubleFunction
  • IntToLongFunction
  • … És ugyanazok az alternatívák Hosszú és Páros

Ezenkívül a 2-es aritív funkciók speciális típusai:

  • BiConsumer
  • BiPredicate
  • BinaryOperator
  • BiFunction

Ennek eredményeként az egész csomag 44 funkcionális típust tartalmaz, amelyek minden bizonnyal zavaróak lehetnek.

6. Ellenőrzött kivételek és lambda kifejezések

Az ellenőrzött kivételek már a Java 8 előtt is problémás és vitatott kérdés volt. A Java 8 érkezése óta felmerült az új kérdés.

Az ellenőrzött kivételeket azonnal be kell fogni, vagy be kell jelenteni. Mivel java.util.function A funkcionális interfészek nem jelentenek dobási kivételt, az ellenőrzött kivételt dobó kód meghiúsul az összeállítás során:

static void writeToFile (Integer integer) dobja az IOException {// logikát, hogy írjon fájlba, amely az IOExceptiont dobja}
Egész számok = tömbök. AsList (3, 9, 7, 0, 10, 20); integers.forEach (i -> writeToFile (i));

A probléma leküzdésének egyik módja az, ha bejelöli a kivételt a próbáld elkapni blokkolja és visszadobja RuntimeException:

Egész számok = tömbök. AsList (3, 9, 7, 0, 10, 20); integers.forEach (i -> {próbáld meg {writeToFile (i);} catch (IOException e) {dobj új RuntimeException (e);}});

Ez működni fog. Dobás azonban RuntimeException ellentmond az ellenőrzött kivétel céljának, és az egész kódot tekercseli a kazánlap kódjával, amelyet lambda kifejezések felhasználásával próbálunk csökkenteni. Az egyik hacky megoldás az, hogy támaszkodunk a sunyi-dobó hackre.

Egy másik megoldás egy fogyasztói funkcionális felület megírása - amely kivételt jelenthet:

@FunctionalInterface nyilvános felület ThrowingConsumer {void accept (T t) dob E; }
statikus Consumer throwingConsumerWrapper (ThrowingConsumer throwingConsumer) {return i -> {try {throwingConsumer.accept (i); } catch (Exception ex) {dobjon új RuntimeException (ex); }}; }

Sajnos az ellenőrzött kivételt továbbra is futásidejű kivételbe csomagoljuk.

Végül a probléma mélyreható megoldása és magyarázata érdekében a következő mély merülést tárhatjuk fel: Kivételek a Java 8 Lambda kifejezésekben.

8. Következtetés

Ebben a gyors írásban megvitattuk a Java 8 néhány hátrányát.

Míg közülük szándékos tervezési döntéseket hoztak a Java nyelvű építészek, és sok esetben van megoldás vagy alternatív megoldás; tudnunk kell a lehetséges problémáikat és korlátaikat.