Undantagshantering i Stream API
Att hantera undantag i Stream API kräver en särskild metod. Till skillnad från traditionella loopar, där ett try-catch-block kan placeras inuti loopens kropp, arbetar streams deklarativt, vilket gör undantagshantering inom dem mer komplex.
Om ett undantag inte hanteras avbryts hela strömbehandlingen. I denna sektion utforskas rätt sätt att fånga och hantera undantag i Stream API.
Problemet med undantagshantering
Anta att vår onlinebutik har en getTotal()-metod som kan kasta ett undantag om orderdata är korrupt eller saknas. Till exempel kan en order laddas från en database där total amount är lagrad som 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;
}
}
Nu, om någon order har ett totalbelopp mindre än 0, kommer hela Stream API-processen att avslutas med ett undantag.
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> user.getOrders().stream()
.filter(order -> order.getTotal() >= 10000)
.count() >= 3)
.toList();
Men det finns ett problem—denna kod kommer inte ens att köras eftersom du inte hanterar de undantag som kan uppstå i getTotal()-metoden. Låt oss därför titta på hur du kan hantera undantag i Stream API.
Hantering av undantag i Stream API
Eftersom try-catch inte kan användas direkt inuti lambdas, finns det flera strategier för undantagshantering i Stream API.
En metod är att fånga undantaget direkt i map() och ersätta det med ett bearbetat resultat:
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();
Inuti mapToDouble() fångas undantaget och en RuntimeException kastas, där det anges vilken användare som orsakade problemet. Denna metod är användbar när det är nödvändigt att omedelbart stoppa exekveringen och snabbt identifiera problemet.
Hoppa över element med fel
Ibland vill du inte stoppa hela processen när ett fel uppstår—du behöver bara hoppa över problematiska element. För att uppnå detta kan du använda filter() med undantagshantering:
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();
Om ett fel uppstår i mapToDouble(Order::getTotal), skulle normalt hela orderströmbehandlingen avbrytas. Men try-catch-blocket inuti filter() förhindrar detta och säkerställer att endast den problematiska användaren utesluts från den slutliga listan.
Undantagshantering med en Wrapper
För att göra vår kod mer robust kan du skapa en wrapper-metod som möjliggör hantering av undantag inuti lambdas samtidigt som de fångas automatiskt.
Java tillåter inte att en Function<T, R> kastar kontrollerade undantag. Om ett undantag uppstår inuti apply(), måste du antingen hantera det inom metoden eller kapsla in det i en RuntimeException, vilket gör koden mer komplex. För att förenkla detta, låt oss definiera ett anpassat funktionellt gränssnitt:
@FunctionalInterface
interface ThrowingFunction<T, R> {
R apply(T t) throws Exception;
}
Detta gränssnitt fungerar på liknande sätt som Function<T, R> men tillåter att apply() kastar ett undantag.
Nu ska vi skapa en klass ExceptionWrapper med en metod wrap() som konverterar en ThrowingFunction<T, R> till en standard Function<T, R> och accepterar en andra parameter som anger ersättningsvärdet vid ett undantag:
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;
}
};
}
}
Metoden wrap() tar en ThrowingFunction<T, R> och omvandlar den till en standard Function<T, R> samtidigt som undantag hanteras. Om ett fel uppstår loggas meddelandet och det angivna standardvärdet returneras.
Användning i Stream API
Anta att du har en lista med användare i en nätbutik och behöver hitta aktiva användare som har minst tre beställningar värda mer än 10 000. Om en beställning har ett negativt belopp vill du dock inte stoppa strömmen—du returnerar helt enkelt 0 som en indikation på att priset var ogiltigt.
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; } }; } }
Nu, om en order innehåller ett negativt belopp, så avbryts inte programmet utan loggar istället ett fel och ersätter det med 0,0. Detta gör databehandlingen mer robust och praktisk för verkliga tillämpningar.
Tack för dina kommentarer!
Fråga AI
Fråga AI
Fråga vad du vill eller prova någon av de föreslagna frågorna för att starta vårt samtal
Awesome!
Completion rate improved to 2.33
Undantagshantering i Stream API
Svep för att visa menyn
Att hantera undantag i Stream API kräver en särskild metod. Till skillnad från traditionella loopar, där ett try-catch-block kan placeras inuti loopens kropp, arbetar streams deklarativt, vilket gör undantagshantering inom dem mer komplex.
Om ett undantag inte hanteras avbryts hela strömbehandlingen. I denna sektion utforskas rätt sätt att fånga och hantera undantag i Stream API.
Problemet med undantagshantering
Anta att vår onlinebutik har en getTotal()-metod som kan kasta ett undantag om orderdata är korrupt eller saknas. Till exempel kan en order laddas från en database där total amount är lagrad som 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;
}
}
Nu, om någon order har ett totalbelopp mindre än 0, kommer hela Stream API-processen att avslutas med ett undantag.
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> user.getOrders().stream()
.filter(order -> order.getTotal() >= 10000)
.count() >= 3)
.toList();
Men det finns ett problem—denna kod kommer inte ens att köras eftersom du inte hanterar de undantag som kan uppstå i getTotal()-metoden. Låt oss därför titta på hur du kan hantera undantag i Stream API.
Hantering av undantag i Stream API
Eftersom try-catch inte kan användas direkt inuti lambdas, finns det flera strategier för undantagshantering i Stream API.
En metod är att fånga undantaget direkt i map() och ersätta det med ett bearbetat resultat:
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();
Inuti mapToDouble() fångas undantaget och en RuntimeException kastas, där det anges vilken användare som orsakade problemet. Denna metod är användbar när det är nödvändigt att omedelbart stoppa exekveringen och snabbt identifiera problemet.
Hoppa över element med fel
Ibland vill du inte stoppa hela processen när ett fel uppstår—du behöver bara hoppa över problematiska element. För att uppnå detta kan du använda filter() med undantagshantering:
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();
Om ett fel uppstår i mapToDouble(Order::getTotal), skulle normalt hela orderströmbehandlingen avbrytas. Men try-catch-blocket inuti filter() förhindrar detta och säkerställer att endast den problematiska användaren utesluts från den slutliga listan.
Undantagshantering med en Wrapper
För att göra vår kod mer robust kan du skapa en wrapper-metod som möjliggör hantering av undantag inuti lambdas samtidigt som de fångas automatiskt.
Java tillåter inte att en Function<T, R> kastar kontrollerade undantag. Om ett undantag uppstår inuti apply(), måste du antingen hantera det inom metoden eller kapsla in det i en RuntimeException, vilket gör koden mer komplex. För att förenkla detta, låt oss definiera ett anpassat funktionellt gränssnitt:
@FunctionalInterface
interface ThrowingFunction<T, R> {
R apply(T t) throws Exception;
}
Detta gränssnitt fungerar på liknande sätt som Function<T, R> men tillåter att apply() kastar ett undantag.
Nu ska vi skapa en klass ExceptionWrapper med en metod wrap() som konverterar en ThrowingFunction<T, R> till en standard Function<T, R> och accepterar en andra parameter som anger ersättningsvärdet vid ett undantag:
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;
}
};
}
}
Metoden wrap() tar en ThrowingFunction<T, R> och omvandlar den till en standard Function<T, R> samtidigt som undantag hanteras. Om ett fel uppstår loggas meddelandet och det angivna standardvärdet returneras.
Användning i Stream API
Anta att du har en lista med användare i en nätbutik och behöver hitta aktiva användare som har minst tre beställningar värda mer än 10 000. Om en beställning har ett negativt belopp vill du dock inte stoppa strömmen—du returnerar helt enkelt 0 som en indikation på att priset var ogiltigt.
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; } }; } }
Nu, om en order innehåller ett negativt belopp, så avbryts inte programmet utan loggar istället ett fel och ersätter det med 0,0. Detta gör databehandlingen mer robust och praktisk för verkliga tillämpningar.
Tack för dina kommentarer!