Výzvy v prostredí Java 8

1. Prehľad

Java 8 predstavila niekoľko nových funkcií, ktoré sa väčšinou točili okolo používania výrazov lambda. V tomto rýchlom článku sa pozrieme na tienisté stránky niektorých z nich.

Aj keď nejde o úplný zoznam, je to subjektívna zbierka najbežnejších a najpopulárnejších sťažností týkajúcich sa nových funkcií v prostredí Java 8.

2. Stream Java 8 a oblasť vlákien

Po prvé, paralelné toky sú určené na to, aby umožnili ľahké paralelné spracovanie sekvencií, a to pri jednoduchých scenároch funguje celkom dobre.

Stream používa predvolené, bežné ForkJoinPool - rozdeľuje sekvencie na menšie kúsky a vykonáva operácie pomocou viacerých vlákien.

Má to však háčik. Nie je to dobrý spôsob upresnite ktoré ForkJoinPool použit a preto, ak sa jedno z vlákien zasekne, všetky ostatné, ktoré používajú zdieľaný fond, budú musieť počkať na dokončenie dlhotrvajúcich úloh.

Našťastie existuje riešenie tohto problému:

ForkJoinPool forkJoinPool = nový ForkJoinPool (2); forkJoinPool.submit (() -> / * nejaké potrubie paralelného toku * /) .get ();

Týmto sa vytvorí nový, samostatný ForkJoinPool a všetky úlohy generované paralelným prúdom budú používať zadaný fond a nie v zdieľanom, predvolenom.

Stojí za zmienku, že existuje ešte jeden potenciálny úlovok: „Táto technika zadania úlohy do fondu fork-join, spustenia paralelného toku v tomto fonde, je implementačným„ trikom “a nie je zaručené, že bude fungovať“, podľa Stuart Marks - vývojára Java a OpenJDK z Oracle. Pri použití tejto techniky je potrebné mať na pamäti dôležitú nuansu.

3. Znížená laditeľnosť

Nový štýl kódovania zatiaľ zjednodušuje náš zdrojový kódpri ladení môže spôsobiť bolesti hlavy.

Najskôr sa pozrime na tento jednoduchý príklad:

public static int getLength (String input) {if (StringUtils.isEmpty (input) {throw new IllegalArgumentException ();} return input.length ();} List lengths = new ArrayList (); for (String name: Arrays.asList ( args)) {lengths.add (getLength (name));}

Toto je štandardný imperatívny kód Java, ktorý je samozrejmý.

Ak prejdeme naprázdno String ako vstup - vo výsledku - kód vyvolá výnimku a v konzole ladenia môžeme vidieť:

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

Poďme teraz znova napísať ten istý kód pomocou Stream API a uvidíme, čo sa stane, keď bude prázdne String dostane sa:

Dĺžka streamu = names.stream () .map (name -> getLength (name));

Zásobník hovorov bude vyzerať takto:

at LmbdaMain.getLength (LmbdaMain.java:19) at LmbdaMain.lambda $ 0 (LmbdaMain.java:37) at LmbdaMain $$ Lambda $ 1 / 821270929.apply (Unknown Source) at java.util.stream.ReferencePipeline $ 3 $ 1.accept ( ReferencePipeline.java:193) na java.util.Spliterators $ ArraySpliterator.forEachRemaining (Spliterators.java:948) na java.util.stream.AbstractPipeline.copyInto (AbstractPipeline.java:512) na java.util.stream.AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:502) na java.util.stream.ReduceOps $ ReduceOp.evaluateSequential (ReduceOps.java:708) na java.util.stream.AbstractPipeline.evaluate (AbstractPipeline.java:234) na java.util.stream. LongPipeline.reduce (LongPipeline.java:438) na java.util.stream.LongPipeline.sum (LongPipeline.java:396) na java.util.stream.ReferencePipeline.count (ReferencePipeline.java:526) na LmbdaMain.main (LmbdaMain .java: 39)

To je cena, ktorú platíme za využitie viacerých abstrakčných vrstiev v našom kóde. IDE však už vyvinuli solídne nástroje na ladenie streamov Java.

4. Vrátenie metód Nulový alebo Voliteľné

Voliteľné bola zavedená v prostredí Java 8, aby poskytla typovo bezpečný spôsob vyjadrenia voliteľnosti.

Voliteľné, výslovne označuje, že návratná hodnota nemusí byť prítomná. Preto volanie metódy môže vrátiť hodnotu a Voliteľné sa používa na zabalenie tejto hodnoty dovnútra - čo sa ukázalo ako užitočné.

Kvôli spätnej kompatibilite Java sme bohužiaľ niekedy skončili tak, že API v rozhraní Java zmiešali dva rôzne konvencie. V tej istej triede nájdeme metódy vracajúce nuly aj metódy vracajúce Voliteľné.

5. Príliš veľa funkčných rozhraní

V java.util.funkcia balíček, máme kolekciu cieľových typov pre výrazy lambda. Môžeme ich rozlíšiť a zoskupiť ako:

  • Spotrebiteľ - predstavuje operáciu, ktorá vezme niektoré argumenty a nevráti žiadny výsledok
  • Funkcia - predstavuje funkciu, ktorá vezme niekoľko argumentov a vytvorí výsledok
  • Prevádzkovateľ - predstavuje operáciu s niektorými argumentmi typu a vracia výsledok rovnakého typu ako operandy
  • Predikát - predstavuje predikát (boolovský-hodnotená funkcia) niektorých argumentov
  • Dodávateľ - predstavuje dodávateľa, ktorý neberie žiadne argumenty a vracia výsledky

Ďalej máme ďalšie typy pre prácu s primitívmi:

  • IntConsumer
  • IntFunction
  • IntPredicate
  • IntSupplier
  • IntToDoubleFunction
  • IntToLongFunction
  • ... a rovnaké alternatívy pre Longs a Štvorhra

Ďalej špeciálne typy pre funkcie s arity 2:

  • BiConsumer
  • BiPredicate
  • BinaryOperator
  • BiFunkcia

Výsledkom je, že celý balík obsahuje 44 funkčných typov, ktoré môžu určite byť neprehľadné.

6. Skontrolované výnimky a výrazy lambda

Skontrolované výnimky boli už pred jazykom Java 8 problematickým a kontroverzným problémom. Od príchodu Java 8 vzniklo nové vydanie.

Skontrolované výnimky musia byť buď okamžite chytené, alebo deklarované. Odkedy java.util.funkcia funkčné rozhrania nedeklarujú vrhacie výnimky, kód, ktorý vrhne skontrolovanú výnimku, sa pri kompilácii nepodarí:

static void writeToFile (Integer integer) vyvolá IOException {// logiku pre zápis do súboru, ktorý vyvolá IOException}
Zoznam celých čísel = Arrays.asList (3, 9, 7, 0, 10, 20); integers.forEach (i -> writeToFile (i));

Jedným zo spôsobov, ako tento problém prekonať, je zabaliť zaškrtnutú výnimku do a Skús chytiť zablokovať a znova vytvoriť RuntimeException:

Zoznam celých čísel = Arrays.asList (3, 9, 7, 0, 10, 20); integers.forEach (i -> {try {writeToFile (i);} catch (IOException e) {throw new RuntimeException (e);}});

Toto bude fungovať. Avšak vhadzovanie RuntimeException je v rozpore s účelom skontrolovanej výnimky a vytvorí celý kód zabalený do štandardného kódu, ktorý sa snažíme obmedziť využitím výrazov lambda. Jedným z hackerských riešení je spoľahnúť sa na hacking Sneaky-throws.

Ďalším riešením je napísať spotrebiteľské funkčné rozhranie - ktoré môže vyvolať výnimku:

@FunctionalInterface verejné rozhranie ThrowingConsumer {void accept (T t) hodí E; }
statický spotrebiteľ throwingConsumerWrapper (ThrowingConsumer throwingConsumer) {return i -> {try {throwingConsumer.accept (i); } catch (Exception ex) {throw new RuntimeException (ex); }}; }

Skontrolovanú výnimku bohužiaľ stále zabalíme do výnimky za behu.

Na záver, pre hĺbkové riešenie a vysvetlenie problému, môžeme preskúmať nasledujúce hlboké ponory: Výnimky v Java 8 Lambda Expressions.

8. Záver

V tomto rýchlom zápise sme prediskutovali niektoré nevýhody Java 8.

Zatiaľ čo niektoré z nich boli zámernými návrhovými voľbami, ktoré urobili architekti jazyka Java, a v mnohých prípadoch existuje riešenie alebo alternatívne riešenie; Musíme si byť vedomí ich možných problémov a obmedzení.