Utfordringer i Java 8

1. Oversikt

Java 8 introduserte noen nye funksjoner, som hovedsakelig dreide seg om bruk av lambdauttrykk. I denne raske artikkelen skal vi se på ulemper med noen av dem.

Og selv om dette ikke er en fullstendig liste, er det en subjektiv samling av de vanligste og mest populære klagene angående nye funksjoner i Java 8.

2. Java 8 Stream and Thread Pool

Først og fremst er Parallel Streams ment for å gjøre enkel parallellbehandling av sekvenser mulig, og det fungerer ganske greit for enkle scenarier.

Stream bruker standard, vanlig ForkJoinPool - deler sekvenser i mindre biter og utfører operasjoner ved hjelp av flere tråder.

Imidlertid er det en fangst. Det er ingen god måte å spesifiser hvilken ForkJoinPool å bruke og derfor, hvis en av trådene setter seg fast, må alle de andre ved å bruke det delte bassenget vente på at de langvarige oppgavene skal fullføres.

Heldigvis er det en løsning for det:

ForkJoinPool forkJoinPool = ny ForkJoinPool (2); forkJoinPool.submit (() -> / * noen parallelle strømrørledninger * /) .get ();

Dette vil skape en ny, separat ForkJoinPool og alle oppgaver generert av parallellstrømmen vil bruke den spesifiserte poolen og ikke i den delte, standardoppgaven.

Det er verdt å merke seg at det er en annen potensiell fangst: "Denne teknikken for å sende en oppgave til et gaffel-sammenkoplingsbasseng, å kjøre den parallelle strømmen i det bassenget, er et" triks "for implementering og fungerer ikke garantert", ifølge Stuart Marks - Java og OpenJDK-utvikler fra Oracle. En viktig nyanse å huske på når du bruker denne teknikken.

3. Redusert feilsøking

Den nye kodestilen forenkler kildekoden vår ennåkan forårsake hodepine mens du feilsøker det.

La oss først se på dette enkle eksemplet:

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));}

Dette er en standard viktig Java-kode som er selvforklarende.

Hvis vi passerer tomme String som en inngang - som et resultat - koden vil kaste et unntak, og i feilsøkingskonsollen kan vi se:

på LmbdaMain.getLength (LmbdaMain.java:19) på LmbdaMain.main (LmbdaMain.java:34)

La oss nå skrive om den samme koden ved hjelp av Stream API og se hva som skjer når det er tomt String blir bestått:

Streamlengder = names.stream () .map (navn -> getLength (navn));

Samtalestakken vil se ut som:

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

Det er prisen vi betaler for å utnytte flere abstraksjonslag i koden vår. Imidlertid har IDE allerede utviklet solide verktøy for feilsøking av Java-strømmer.

4. Metoder tilbake Null eller Valgfri

Valgfri ble introdusert i Java 8 for å gi en typesikker måte å uttrykke valgfrihet på.

Valgfri, angir eksplisitt at returverdien kanskje ikke er til stede. Derfor kan det å ringe en metode returnere en verdi, og Valgfri brukes til å pakke den verdien inn - som viste seg å være nyttig.

Dessverre, på grunn av Java-bakoverkompatibilitet, endte vi noen ganger med Java APIer som blandet to forskjellige konvensjoner. I samme klasse kan vi finne metoder som returnerer null og metoder som returnerer Ekstrautstyr.

5. For mange funksjonelle grensesnitt

I java.util.funksjon pakken, har vi en samling måltyper for lambdauttrykk. Vi kan skille og gruppere dem som:

  • Forbruker - representerer en operasjon som tar noen argumenter og ikke gir noe resultat
  • Funksjon - representerer en funksjon som tar noen argumenter og gir et resultat
  • Operatør - representerer en operasjon på noen type argumenter og returnerer et resultat av samme type som operandene
  • Predikere - representerer et predikat (boolskverdsatt funksjon) av noen argumenter
  • Leverandør - representerer en leverandør som ikke tar argumenter og returnerer resultater

I tillegg har vi flere typer for å jobbe med primitiver:

  • IntConsumer
  • IntFunction
  • IntPredicate
  • IntSupplier
  • IntToDoubleFunction
  • IntToLongFunction
  • ... og samme alternativer for Lengter og Dobler

Videre spesielle typer for funksjoner med arity av 2:

  • BiConsumer
  • BiPredicate
  • BinaryOperator
  • BiFunction

Som et resultat inneholder hele pakken 44 funksjonstyper, som absolutt kan begynne å være forvirrende.

6. Sjekket unntak og Lambda-uttrykk

Sjekket unntak har allerede vært et problematisk og kontroversielt problem før Java 8. Siden ankomsten av Java 8 oppstod den nye utgaven.

Avmerkede unntak må enten fanges umiddelbart eller erklæres. Siden java.util.funksjon funksjonelle grensesnitt erklærer ikke kaste unntak, kode som kaster avmerket unntak mislykkes under kompilering:

statisk ugyldig writeToFile (heltall) kaster IOException {// logikk for å skrive til fil som kaster IOException}
Liste heltall = Arrays.asList (3, 9, 7, 0, 10, 20); heltall.forEach (i -> writeToFile (i));

En måte å løse dette problemet på er å pakke inn merket unntak i a prøvefangst blokker og kast RuntimeException:

Liste heltall = Arrays.asList (3, 9, 7, 0, 10, 20); integers.forEach (i -> {try {writeToFile (i);} catch (IOException e) {throw new RuntimeException (e);}});

Dette vil fungere. Imidlertid kaste RuntimeException motsier hensikten med avkrysset unntak og gjør at hele koden er pakket med kjeleplatekode, som vi prøver å redusere ved å utnytte lambda-uttrykk. En av hacky-løsningene er å stole på sneak-throw-hacket.

En annen løsning er å skrive et forbrukerfunksjonelt grensesnitt - som kan gi et unntak:

@FunctionalInterface offentlig grensesnitt ThrowingConsumer {void accept (T t) kaster E; }
statisk Consumer throwingConsumerWrapper (ThrowingConsumer throwingConsumer) {return i -> {try {throwingConsumer.accept (i); } fange (Unntak ex) {kast ny RuntimeException (ex); }}; }

Dessverre pakker vi fremdeles det avmerkede unntaket i et unntak for kjøretid.

Til slutt, for en grundig løsning og forklaring av problemet, kan vi utforske følgende dypdykk: Unntak i Java 8 Lambda Expressions.

8. Konklusjon

I denne raske oppskriften diskuterte vi noen av ulempene ved Java 8.

Mens noen av dem var bevisste designvalg gjort av Java-språkarkitekter, og i mange tilfeller er det en løsning eller alternativ løsning; vi trenger å være klar over deres mulige problemer og begrensninger.


$config[zx-auto] not found$config[zx-overlay] not found