Iznimke u Java 8 Lambda izrazima

1. Pregled

U Javi 8, Lambda Expressions počeli su olakšavati funkcionalno programiranje pružajući koncizan način izražavanja ponašanja. Međutim Funkcionalna sučelja koje pruža JDK, ne bave se baš iznimkama - a kod postajanja opširnim i glomaznim kada je riječ o rukovanju njima.

U ovom ćemo članku istražiti neke načine rješavanja iznimaka prilikom pisanja lambda izraza.

2. Rukovanje neprovjerenim iznimkama

Prvo, shvatimo problem na primjeru.

Imamo Popis i želimo podijeliti konstantu, recimo 50 sa svakim elementom ovog popisa i ispisati rezultate:

Popis cijelih brojeva = Arrays.asList (3, 9, 7, 6, 10, 20); cijeli brojevi.forEach (i -> System.out.println (50 / i));

Ovaj izraz djeluje, ali postoji jedan problem. Ako je bilo koji od elemenata na popisu 0, tada dobivamo ArithmeticException: / po nuli. Popravimo to pomoću tradicionalne pokušaj uhvatiti blokirati tako da zabilježimo bilo koju takvu iznimku i nastavimo izvršavanje za sljedeće elemente:

Popis cijelih brojeva = Arrays.asList (3, 9, 7, 0, 10, 20); integers.forEach (i -> {try {System.out.println (50 / i);} catch (ArithmeticException e) {System.err.println ("Došlo je do aritmetičke iznimke: + e.getMessage ());}} );

Korištenje pokušaj uhvatiti rješava problem, ali jezgrovitost a Lambda Izraz je izgubljeno i to više nije mala funkcija kakva bi trebala biti.

Za rješavanje ovog problema možemo pisati lambda omot za lambda funkciju. Pogledajmo kod kako bismo vidjeli kako on funkcionira:

static Consumer lambdaWrapper (Potrošački potrošač) {return i -> {try {consumer.accept (i); } catch (ArithmeticException e) {System.err.println ("Došlo je do aritmetičke iznimke:" + e.getMessage ()); }}; }
Popis cijelih brojeva = Arrays.asList (3, 9, 7, 0, 10, 20); cijeli brojevi.zaEach (lambdaWrapper (i -> System.out.println (50 / i)));

Prvo smo napisali metodu omota koji će biti odgovoran za rukovanje iznimkom, a zatim smo ovoj metodi proslijedili lambda izraz kao parametar.

Omotač metoda radi kako se očekivalo, ali možete tvrditi da u osnovi uklanja pokušaj uhvatiti blok iz lambda izraza i premještanje na drugu metodu i to ne smanjuje stvarni broj redaka koda koji se zapisuje.

To vrijedi u ovom slučaju kada je omot specifičan za određeni slučaj upotrebe, ali možemo upotrijebiti generičke lijekove za poboljšanje ove metode i koristiti ih za niz drugih scenarija:

static Consumer consumerWrapper (Potrošački potrošač, Klasa klazz) {return i -> {try {consumer.accept (i); } catch (Iznimka ex) {try {E exCast = clazz.cast (ex); System.err.println ("Dogodila se iznimka:" + exCast.getMessage ()); } catch (ClassCastException ccEx) {throw ex; }}}; }
Popis cijelih brojeva = Arrays.asList (3, 9, 7, 0, 10, 20); integers.forEach (consumerWrapper (i -> System.out.println (50 / i), ArithmeticException.class));

Kao što vidimo, ova iteracija naše metode omotača uzima dva argumenta, lambda izraz i vrstu Iznimka biti uhvaćen. Ovaj lambda omot sposoban je za obradu svih vrsta podataka, a ne samo Cijeli brojevii uhvatite bilo koju vrstu iznimke, a ne superklasu Iznimka.

Također, primijetite da smo promijenili naziv metode iz lambdaWrapper do potrošačOmotač. To je zato što ova metoda obrađuje samo lambda izraze za Funkcionalno sučelje tipa Potrošač. Možemo napisati slične metode omota za druga funkcionalna sučelja poput Funkcija, BiFunction, BiConsumer i tako dalje.

3. Rukovanje provjerenim iznimkama

Izmijenimo primjer iz prethodnog odjeljka i umjesto ispisa na konzolu, zapišite u datoteku.

static void writeToFile (Integer integer) baca IOException {// logika za pisanje u datoteku koja baca IOException}

Imajte na umu da gornja metoda može baciti IOException.

Popis cijelih brojeva = Arrays.asList (3, 9, 7, 0, 10, 20); cijeli brojevi.forEach (i -> writeToFile (i));

Pri kompilaciji dobivamo pogrešku:

java.lang.Error: Neriješen problem kompilacije: Neobrađena vrsta iznimke IOException

Jer IOException je provjerena iznimka, moramo to izričito riješiti. Imamo dvije mogućnosti.

Prvo, iznimku možemo jednostavno izbaciti izvan svoje metode i pobrinuti se negdje drugdje.

Alternativno, možemo to riješiti unutar metode koja koristi lambda izraz.

Istražimo obje mogućnosti.

3.1. Bacanje provjerene iznimke iz Lambda Expressionsa

Pogledajmo što će se dogoditi kad proglasimo IOException na glavni metoda:

public static void main (String [] args) baca IOException {List integers = Arrays.asList (3, 9, 7, 0, 10, 20); cijeli brojevi.forEach (i -> writeToFile (i)); }

Još, dobivamo istu pogrešku neobrađenih IOException tijekom sastavljanja.

java.lang.Error: Neriješen problem kompilacije: Neobrađena vrsta iznimke IOException

To je zato što su lambda izrazi slični anonimnim unutarnjim klasama.

U našem slučaju, writeToFile metoda je provedba Potrošač funkcionalno sučelje.

Pogledajmo PotrošačDefinicija:

@FunctionalInterface javno sučelje Potrošač {void accept (T t); }

Kao što možemo vidjeti prihvatiti metoda ne prijavljuje provjerenu iznimku. To je razlog zašto writeToFile nije dopušteno baciti IOException.

Najjednostavniji način bio bi korištenje a pokušaj uhvatiti blok, zamotajte provjerenu iznimku u neprovjerenu iznimku i vratite je:

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

Ovo dobiva kod za kompajliranje i pokretanje. Međutim, ovaj pristup uvodi isto pitanje o kojem smo već raspravljali u prethodnom odjeljku - to je opširno i glomazno.

Možemo biti bolji od toga.

Stvorimo prilagođeno funkcionalno sučelje s jednim prihvatiti metoda koja baca iznimku.

@FunctionalInterface javno sučelje ThrowingConsumer {void accept (T t) baca E; }

A sada, implementirajmo metodu omotača koja može povratiti iznimku:

static Consumer throwingConsumerWrapper (ThrowingConsumer throwingConsumer) {return i -> {try {throwingConsumer.accept (i); } catch (Exception ex) {throw new RuntimeException (ex); }}; }

Napokon, možemo pojednostavniti način na koji koristimo writeToFile metoda:

Popis cijelih brojeva = Arrays.asList (3, 9, 7, 0, 10, 20); cijeli brojevi.forEach (throwingConsumerWrapper (i -> writeToFile (i)));

Ovo je još uvijek svojevrsno rješenje, ali krajnji rezultat izgleda prilično čisto i definitivno ga je lakše održavati.

Oboje, ThrowingConsumer i throwingConsumerWrapper su generički i mogu se lako ponovno koristiti na različitim mjestima naše aplikacije.

3.2. Rukovanje provjerenom iznimkom u Lambda Expressionu

U ovom posljednjem odjeljku izmijenit ćemo omot kako bi obrađivao provjerene iznimke.

Od našeg ThrowingConsumer sučelje koristi generičke lijekove, lako možemo riješiti bilo koju određenu iznimku.

static Consumer handlingConsumerWrapper (ThrowingConsumer throwingConsumer, Class exceptionClass) {return i -> {try {throwingConsumer.accept (i); } catch (Iznimka ex) {try {E exCast = exceptionClass.cast (ex); System.err.println ("Dogodila se iznimka:" + exCast.getMessage ()); } catch (ClassCastException ccEx) {throw new RuntimeException (ex); }}}; }

Pogledajmo kako ga koristiti u praksi:

Popis cijelih brojeva = Arrays.asList (3, 9, 7, 0, 10, 20); integers.forEach (handlingConsumerWrapper (i -> writeToFile (i), IOException.class));

Imajte na umu da je gornji kod samo ručke IOException, dok se bilo koja druga vrsta iznimke ponovno uvodi kao a RuntimeException .

4. Zaključak

U ovom smo članku pokazali kako postupati s određenom iznimkom u lambda izrazu bez gubitka sažetosti uz pomoć metoda omota. Također smo naučili kako pisati alternative bacanja za funkcionalna sučelja prisutna u JDK kako bi se bacilo ili obradilo provjerenu iznimku.

Drugi bi način bio istražiti hakiranje podlih bacanja.

Kompletni izvorni kod funkcionalnog sučelja i metoda omotača možete preuzeti odavde, a ispitne satove odavde, na Githubu.

Ako tražite izvanredna radna rješenja, vrijedi provjeriti projekt ThrowingFunction.