Poikkeusten Käsittely Stream API:ssa
Poikkeusten käsittely Stream API:ssa vaatii erityistä lähestymistapaa. Toisin kuin perinteisissä silmukoissa, joissa try-catch-lohko voidaan sijoittaa silmukan sisälle, streamit toimivat deklaratiivisesti, mikä tekee poikkeusten käsittelystä niissä monimutkaisempaa.
Jos poikkeusta ei käsitellä, se keskeyttää koko stream-käsittelyn. Tässä osiossa tutustut oikeaan tapaan käsitellä ja ottaa kiinni poikkeuksia Stream API:ssa.
Poikkeuksen käsittelyn ongelma
Oletetaan, että verkkokaupassamme on getTotal()-metodi, joka voi heittää poikkeuksen, jos tilausdata on vioittunut tai puuttuu. Esimerkiksi tilaus voidaan ladata tietokannasta, jossa kokonaissumma on tallennettu arvolla null.
class Order {
private final double total;
public Order(double total) {
this.total = total;
}
public double getTotal() throws Exception {
if (total < 0) {
throw new IllegalArgumentException("Invalid order total");
}
return total;
}
}
Nyt, jos jokin order sisältää total-arvon, joka on alle 0, koko Stream API -prosessi keskeytyy poikkeukseen.
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> user.getOrders().stream()
.filter(order -> order.getTotal() >= 10000)
.count() >= 3)
.toList();
Mutta tässä on ongelma—tämä koodi ei edes käynnisty, koska et käsittele mahdollisia poikkeuksia, joita voi esiintyä getTotal()-metodissa. Tarkastellaan siis, miten poikkeusten käsittely onnistuu Stream API:ssa.
Poikkeusten käsittely Stream API:ssa
Koska try-catch-rakennetta ei voi käyttää suoraan lambdoissa, on olemassa useita strategioita poikkeusten käsittelyyn Stream API:ssa.
Yksi tapa on käsitellä poikkeus suoraan map()-metodissa ja korvata se käsitellyllä tuloksella:
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> user.getOrders().stream()
.mapToDouble(order -> {
try {
return order.getTotal();
} catch (IllegalArgumentException e) {
throw new RuntimeException("Error in user's order: " + user, e);
}
})
.filter(total -> total >= 10000)
.count() >= 3)
.toList();
mapToDouble()-metodin sisällä poikkeus otetaan kiinni ja heitetään RuntimeException, jossa määritellään, mikä käyttäjä aiheutti ongelman. Tämä lähestymistapa on hyödyllinen, kun suoritus halutaan keskeyttää välittömästi ja ongelma tunnistaa nopeasti.
Virheellisten alkioiden ohittaminen
Joskus koko prosessia ei haluta keskeyttää virheen sattuessa—riittää, että ongelmalliset alkiot ohitetaan. Tämän voi toteuttaa käyttämällä filter()-metodia yhdessä poikkeusten käsittelyn kanssa:
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> {
try {
return user.getOrders().stream()
.mapToDouble(Order::getTotal)
.filter(total -> total >= 10000)
.count() >= 3;
} catch (Exception e) {
return false; // Skip users with problematic orders
}
})
.toList();
Jos virhe tapahtuu kohdassa mapToDouble(Order::getTotal), koko tilausten virran käsittely keskeytyisi normaalisti. try-catch-lohko filter()-metodin sisällä kuitenkin estää tämän, jolloin vain ongelmallinen käyttäjä jätetään pois lopullisesta listasta.
Poikkeusten käsittely kääreellä
Koodin luotettavuuden parantamiseksi voidaan luoda kääre-metodi, joka mahdollistaa poikkeusten käsittelyn lambda-lausekkeissa ja sieppaa ne automaattisesti.
Java ei salli, että Function<T, R> heittää tarkistettuja poikkeuksia. Jos poikkeus tapahtuu apply()-menetelmän sisällä, se täytyy joko käsitellä metodissa tai kääriä RuntimeException-poikkeukseen, mikä monimutkaistaa koodia. Tämän yksinkertaistamiseksi määritellään oma funktionaalinen rajapinta:
@FunctionalInterface
interface ThrowingFunction<T, R> {
R apply(T t) throws Exception;
}
Tämä rajapinta toimii samalla tavalla kuin Function<T, R>, mutta sallii apply()-menetelmän heittää poikkeuksen.
Seuraavaksi luodaan ExceptionWrapper-luokka, jossa on wrap()-metodi. Tämä metodi muuntaa ThrowingFunction<T, R>-funktion tavalliseksi Function<T, R>-funktioksi ja ottaa toisena parametrina varmistusarvon poikkeustilanteita varten:
class ExceptionWrapper {
// Wrapper for `Function` to handle exceptions
public static <T, R> Function<T, R> wrap(ThrowingFunction<T, R> function, R defaultValue) {
return t -> {
try {
return function.apply(t);
} catch (Exception e) {
System.out.println(e.getMessage());
return defaultValue;
}
};
}
}
wrap() metodi ottaa vastaan ThrowingFunction<T, R>-funktion ja muuntaa sen tavalliseksi Function<T, R>-funktioksi samalla kun se käsittelee poikkeukset. Jos ilmenee virhe, se kirjaa viestin ja palauttaa määritetyn oletusarvon.
Käyttö Stream API:ssa
Oletetaan, että käytössä on lista käyttäjistä verkkokaupassa, ja tavoitteena on löytää aktiiviset käyttäjät, joilla on vähintään kolme tilausta, joiden arvo on yli 10 000. Jos kuitenkin tilauksella on negatiivinen summa, virran käsittelyä ei haluta keskeyttää—palautetaan yksinkertaisesti 0 merkiksi siitä, että hinta oli virheellinen.
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394package com.example; import java.util.List; import java.util.function.Function; public class Main { public static void main(String[] args) { List<User> users = List.of( new User("Alice", true, List.of(new Order(12000.00), new Order(15000.00), new Order(11000.00))), new User("Bob", true, List.of(new Order(8000.00), new Order(9000.00), new Order(12000.00))), new User("Charlie", false, List.of(new Order(15000.00), new Order(16000.00), new Order(17000.00))), new User("David", true, List.of(new Order(5000.00), new Order(20000.00), new Order(30000.00))), new User("Eve", true, List.of(new Order(null), new Order(10000.00), new Order(10000.00), new Order(12000.00))), new User("Frank", true, List.of(new Order(-5000.00), new Order(10000.00))) // Error: Negative order amount ); List<User> premiumUsers = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .map(ExceptionWrapper.wrap(Order::getTotal, 0.0)) // Using the wrapper function .filter(total -> total >= 10000) .count() >= 3) .toList(); System.out.println("Premium users: " + premiumUsers); } } // The `Order` class represents a customer's order class Order { private final Double total; public Order(Double total) { this.total = total; } public double getTotal() throws Exception { if (total == null || total < 0) { throw new Exception("Error: Order amount cannot be negative or equal to null!"); } return total; } } // The `User` class represents an online store user class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } public String getName() { return name; } @Override public String toString() { return "User{name='" + name + "'}"; } } // Functional interface for handling exceptions in `Function` @FunctionalInterface interface ThrowingFunction<T, R> { R apply(T t) throws Exception; } // A helper class with wrapper methods for exception handling class ExceptionWrapper { // A wrapper for `Function` that catches exceptions public static <T, R> Function<T, R> wrap(ThrowingFunction<T, R> function, R defaultValue) { return t -> { try { return function.apply(t); } catch (Exception e) { System.out.println(e.getMessage()); return defaultValue; } }; } }
Nyt, jos tilaus sisältää negatiivisen summan, ohjelma ei pysähdy vaan kirjaa virheen ja korvaa summan arvolla 0.0. Tämä tekee datan käsittelystä kestävämpää ja käytännöllisempää todellisissa käyttötapauksissa.
Kiitos palautteestasi!
Kysy tekoälyä
Kysy tekoälyä
Kysy mitä tahansa tai kokeile jotakin ehdotetuista kysymyksistä aloittaaksesi keskustelumme
Awesome!
Completion rate improved to 2.33
Poikkeusten Käsittely Stream API:ssa
Pyyhkäise näyttääksesi valikon
Poikkeusten käsittely Stream API:ssa vaatii erityistä lähestymistapaa. Toisin kuin perinteisissä silmukoissa, joissa try-catch-lohko voidaan sijoittaa silmukan sisälle, streamit toimivat deklaratiivisesti, mikä tekee poikkeusten käsittelystä niissä monimutkaisempaa.
Jos poikkeusta ei käsitellä, se keskeyttää koko stream-käsittelyn. Tässä osiossa tutustut oikeaan tapaan käsitellä ja ottaa kiinni poikkeuksia Stream API:ssa.
Poikkeuksen käsittelyn ongelma
Oletetaan, että verkkokaupassamme on getTotal()-metodi, joka voi heittää poikkeuksen, jos tilausdata on vioittunut tai puuttuu. Esimerkiksi tilaus voidaan ladata tietokannasta, jossa kokonaissumma on tallennettu arvolla null.
class Order {
private final double total;
public Order(double total) {
this.total = total;
}
public double getTotal() throws Exception {
if (total < 0) {
throw new IllegalArgumentException("Invalid order total");
}
return total;
}
}
Nyt, jos jokin order sisältää total-arvon, joka on alle 0, koko Stream API -prosessi keskeytyy poikkeukseen.
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> user.getOrders().stream()
.filter(order -> order.getTotal() >= 10000)
.count() >= 3)
.toList();
Mutta tässä on ongelma—tämä koodi ei edes käynnisty, koska et käsittele mahdollisia poikkeuksia, joita voi esiintyä getTotal()-metodissa. Tarkastellaan siis, miten poikkeusten käsittely onnistuu Stream API:ssa.
Poikkeusten käsittely Stream API:ssa
Koska try-catch-rakennetta ei voi käyttää suoraan lambdoissa, on olemassa useita strategioita poikkeusten käsittelyyn Stream API:ssa.
Yksi tapa on käsitellä poikkeus suoraan map()-metodissa ja korvata se käsitellyllä tuloksella:
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> user.getOrders().stream()
.mapToDouble(order -> {
try {
return order.getTotal();
} catch (IllegalArgumentException e) {
throw new RuntimeException("Error in user's order: " + user, e);
}
})
.filter(total -> total >= 10000)
.count() >= 3)
.toList();
mapToDouble()-metodin sisällä poikkeus otetaan kiinni ja heitetään RuntimeException, jossa määritellään, mikä käyttäjä aiheutti ongelman. Tämä lähestymistapa on hyödyllinen, kun suoritus halutaan keskeyttää välittömästi ja ongelma tunnistaa nopeasti.
Virheellisten alkioiden ohittaminen
Joskus koko prosessia ei haluta keskeyttää virheen sattuessa—riittää, että ongelmalliset alkiot ohitetaan. Tämän voi toteuttaa käyttämällä filter()-metodia yhdessä poikkeusten käsittelyn kanssa:
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> {
try {
return user.getOrders().stream()
.mapToDouble(Order::getTotal)
.filter(total -> total >= 10000)
.count() >= 3;
} catch (Exception e) {
return false; // Skip users with problematic orders
}
})
.toList();
Jos virhe tapahtuu kohdassa mapToDouble(Order::getTotal), koko tilausten virran käsittely keskeytyisi normaalisti. try-catch-lohko filter()-metodin sisällä kuitenkin estää tämän, jolloin vain ongelmallinen käyttäjä jätetään pois lopullisesta listasta.
Poikkeusten käsittely kääreellä
Koodin luotettavuuden parantamiseksi voidaan luoda kääre-metodi, joka mahdollistaa poikkeusten käsittelyn lambda-lausekkeissa ja sieppaa ne automaattisesti.
Java ei salli, että Function<T, R> heittää tarkistettuja poikkeuksia. Jos poikkeus tapahtuu apply()-menetelmän sisällä, se täytyy joko käsitellä metodissa tai kääriä RuntimeException-poikkeukseen, mikä monimutkaistaa koodia. Tämän yksinkertaistamiseksi määritellään oma funktionaalinen rajapinta:
@FunctionalInterface
interface ThrowingFunction<T, R> {
R apply(T t) throws Exception;
}
Tämä rajapinta toimii samalla tavalla kuin Function<T, R>, mutta sallii apply()-menetelmän heittää poikkeuksen.
Seuraavaksi luodaan ExceptionWrapper-luokka, jossa on wrap()-metodi. Tämä metodi muuntaa ThrowingFunction<T, R>-funktion tavalliseksi Function<T, R>-funktioksi ja ottaa toisena parametrina varmistusarvon poikkeustilanteita varten:
class ExceptionWrapper {
// Wrapper for `Function` to handle exceptions
public static <T, R> Function<T, R> wrap(ThrowingFunction<T, R> function, R defaultValue) {
return t -> {
try {
return function.apply(t);
} catch (Exception e) {
System.out.println(e.getMessage());
return defaultValue;
}
};
}
}
wrap() metodi ottaa vastaan ThrowingFunction<T, R>-funktion ja muuntaa sen tavalliseksi Function<T, R>-funktioksi samalla kun se käsittelee poikkeukset. Jos ilmenee virhe, se kirjaa viestin ja palauttaa määritetyn oletusarvon.
Käyttö Stream API:ssa
Oletetaan, että käytössä on lista käyttäjistä verkkokaupassa, ja tavoitteena on löytää aktiiviset käyttäjät, joilla on vähintään kolme tilausta, joiden arvo on yli 10 000. Jos kuitenkin tilauksella on negatiivinen summa, virran käsittelyä ei haluta keskeyttää—palautetaan yksinkertaisesti 0 merkiksi siitä, että hinta oli virheellinen.
Main.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394package com.example; import java.util.List; import java.util.function.Function; public class Main { public static void main(String[] args) { List<User> users = List.of( new User("Alice", true, List.of(new Order(12000.00), new Order(15000.00), new Order(11000.00))), new User("Bob", true, List.of(new Order(8000.00), new Order(9000.00), new Order(12000.00))), new User("Charlie", false, List.of(new Order(15000.00), new Order(16000.00), new Order(17000.00))), new User("David", true, List.of(new Order(5000.00), new Order(20000.00), new Order(30000.00))), new User("Eve", true, List.of(new Order(null), new Order(10000.00), new Order(10000.00), new Order(12000.00))), new User("Frank", true, List.of(new Order(-5000.00), new Order(10000.00))) // Error: Negative order amount ); List<User> premiumUsers = users.stream() .filter(User::isActive) .filter(user -> user.getOrders().stream() .map(ExceptionWrapper.wrap(Order::getTotal, 0.0)) // Using the wrapper function .filter(total -> total >= 10000) .count() >= 3) .toList(); System.out.println("Premium users: " + premiumUsers); } } // The `Order` class represents a customer's order class Order { private final Double total; public Order(Double total) { this.total = total; } public double getTotal() throws Exception { if (total == null || total < 0) { throw new Exception("Error: Order amount cannot be negative or equal to null!"); } return total; } } // The `User` class represents an online store user class User { private final String name; private final boolean active; private final List<Order> orders; public User(String name, boolean active, List<Order> orders) { this.name = name; this.active = active; this.orders = orders; } public boolean isActive() { return active; } public List<Order> getOrders() { return orders; } public String getName() { return name; } @Override public String toString() { return "User{name='" + name + "'}"; } } // Functional interface for handling exceptions in `Function` @FunctionalInterface interface ThrowingFunction<T, R> { R apply(T t) throws Exception; } // A helper class with wrapper methods for exception handling class ExceptionWrapper { // A wrapper for `Function` that catches exceptions public static <T, R> Function<T, R> wrap(ThrowingFunction<T, R> function, R defaultValue) { return t -> { try { return function.apply(t); } catch (Exception e) { System.out.println(e.getMessage()); return defaultValue; } }; } }
Nyt, jos tilaus sisältää negatiivisen summan, ohjelma ei pysähdy vaan kirjaa virheen ja korvaa summan arvolla 0.0. Tämä tekee datan käsittelystä kestävämpää ja käytännöllisempää todellisissa käyttötapauksissa.
Kiitos palautteestasi!