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.