Undtagelseshåndtering i Stream API
Håndtering af undtagelser i Stream API kræver en særlig tilgang. I modsætning til traditionelle løkker, hvor et try-catch-blok kan placeres inde i løkkens krop, arbejder streams deklarativt, hvilket gør undtagelseshåndtering mere kompleks.
Hvis en undtagelse ikke håndteres, afbrydes hele stream-behandlingen. I dette afsnit vil du udforske den korrekte måde at fange og håndtere undtagelser i Stream API.
Problemet med undtagelseshåndtering
Antag, at vores onlinebutik har en getTotal()-metode, der kan kaste en undtagelse, hvis ordredata er beskadiget eller mangler. For eksempel kan en ordre være indlæst fra en database, hvor totalbeløbet er gemt 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;
}
}
Hvis en ordre har en total mindre end 0, vil hele Stream API-processen afsluttes med en undtagelse.
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> user.getOrders().stream()
.filter(order -> order.getTotal() >= 10000)
.count() >= 3)
.toList();
Men der er et problem—denne kode vil ikke engang køre, fordi du ikke håndterer de undtagelser, der kan opstå i getTotal()-metoden. Lad os derfor se på, hvordan du kan håndtere undtagelser i Stream API.
Håndtering af undtagelser i Stream API
Da try-catch ikke kan bruges direkte inde i lambdaer, findes der flere strategier til håndtering af undtagelser i Stream API.
En metode er at fange undtagelsen direkte inde i map() og erstatte den med et behandlet 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();
Inde i mapToDouble() fanges undtagelsen, og der kastes en RuntimeException, hvor det angives, hvilken bruger der forårsagede problemet. Denne metode er nyttig, når det er nødvendigt straks at stoppe eksekveringen og hurtigt identificere problemet.
Spring elementer med fejl
Nogle gange er det ikke nødvendigt at stoppe hele processen, når der opstår en fejl—du skal blot springe problematiske elementer over. Dette kan opnås ved at bruge filter() med fejlhåndtering:
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();
Hvis der opstår en fejl i mapToDouble(Order::getTotal), ville hele ordre-stream-processen normalt stoppe. Men try-catch-blokken inde i filter() forhindrer dette og sikrer, at kun den problematiske bruger udelukkes fra den endelige liste.
Undtagelseshåndtering med en Wrapper
For at gøre vores kode mere robust, kan du oprette en wrapper-metode, der muliggør håndtering af undtagelser inde i lambdas samtidig med, at de automatisk fanges.
Java tillader ikke, at en Function<T, R> kaster checked exceptions. Hvis en undtagelse opstår inde i apply(), skal du enten håndtere den inden for metoden eller pakke den ind i en RuntimeException, hvilket gør koden mere kompleks. For at forenkle dette, lad os definere et brugerdefineret funktionelt interface:
@FunctionalInterface
interface ThrowingFunction<T, R> {
R apply(T t) throws Exception;
}
Dette interface fungerer på samme måde som Function<T, R>, men tillader at apply() kan kaste en undtagelse.
Lad os nu oprette en ExceptionWrapper klasse med en wrap() metode, der konverterer en ThrowingFunction<T, R> til en standard Function<T, R> og accepterer en anden parameter, der angiver fallback-værdien i tilfælde af en undtagelse:
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() tager en ThrowingFunction<T, R> og konverterer den til en standard Function<T, R> med håndtering af undtagelser. Hvis der opstår en fejl, logges beskeden og den angivne standardværdi returneres.
Anvendelse i Stream API
Antag, at der findes en liste over brugere i en onlinebutik, og det er nødvendigt at finde aktive brugere, som har mindst tre ordrer med en værdi på over 10.000. Hvis en ordre har et negativt beløb, skal streamen ikke stoppes—der returneres blot 0 som en indikation af, at prisen var ugyldig.
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, hvis en ordre indeholder et negativt beløb, stopper programmet ikke, men logger blot en fejl og erstatter det med 0,0. Dette gør databehandling mere robust og praktisk til virkelige anvendelser.
Tak for dine kommentarer!
Spørg AI
Spørg AI
Spørg om hvad som helst eller prøv et af de foreslåede spørgsmål for at starte vores chat
Awesome!
Completion rate improved to 2.33
Undtagelseshåndtering i Stream API
Stryg for at vise menuen
Håndtering af undtagelser i Stream API kræver en særlig tilgang. I modsætning til traditionelle løkker, hvor et try-catch-blok kan placeres inde i løkkens krop, arbejder streams deklarativt, hvilket gør undtagelseshåndtering mere kompleks.
Hvis en undtagelse ikke håndteres, afbrydes hele stream-behandlingen. I dette afsnit vil du udforske den korrekte måde at fange og håndtere undtagelser i Stream API.
Problemet med undtagelseshåndtering
Antag, at vores onlinebutik har en getTotal()-metode, der kan kaste en undtagelse, hvis ordredata er beskadiget eller mangler. For eksempel kan en ordre være indlæst fra en database, hvor totalbeløbet er gemt 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;
}
}
Hvis en ordre har en total mindre end 0, vil hele Stream API-processen afsluttes med en undtagelse.
List<User> premiumUsers = users.stream()
.filter(User::isActive)
.filter(user -> user.getOrders().stream()
.filter(order -> order.getTotal() >= 10000)
.count() >= 3)
.toList();
Men der er et problem—denne kode vil ikke engang køre, fordi du ikke håndterer de undtagelser, der kan opstå i getTotal()-metoden. Lad os derfor se på, hvordan du kan håndtere undtagelser i Stream API.
Håndtering af undtagelser i Stream API
Da try-catch ikke kan bruges direkte inde i lambdaer, findes der flere strategier til håndtering af undtagelser i Stream API.
En metode er at fange undtagelsen direkte inde i map() og erstatte den med et behandlet 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();
Inde i mapToDouble() fanges undtagelsen, og der kastes en RuntimeException, hvor det angives, hvilken bruger der forårsagede problemet. Denne metode er nyttig, når det er nødvendigt straks at stoppe eksekveringen og hurtigt identificere problemet.
Spring elementer med fejl
Nogle gange er det ikke nødvendigt at stoppe hele processen, når der opstår en fejl—du skal blot springe problematiske elementer over. Dette kan opnås ved at bruge filter() med fejlhåndtering:
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();
Hvis der opstår en fejl i mapToDouble(Order::getTotal), ville hele ordre-stream-processen normalt stoppe. Men try-catch-blokken inde i filter() forhindrer dette og sikrer, at kun den problematiske bruger udelukkes fra den endelige liste.
Undtagelseshåndtering med en Wrapper
For at gøre vores kode mere robust, kan du oprette en wrapper-metode, der muliggør håndtering af undtagelser inde i lambdas samtidig med, at de automatisk fanges.
Java tillader ikke, at en Function<T, R> kaster checked exceptions. Hvis en undtagelse opstår inde i apply(), skal du enten håndtere den inden for metoden eller pakke den ind i en RuntimeException, hvilket gør koden mere kompleks. For at forenkle dette, lad os definere et brugerdefineret funktionelt interface:
@FunctionalInterface
interface ThrowingFunction<T, R> {
R apply(T t) throws Exception;
}
Dette interface fungerer på samme måde som Function<T, R>, men tillader at apply() kan kaste en undtagelse.
Lad os nu oprette en ExceptionWrapper klasse med en wrap() metode, der konverterer en ThrowingFunction<T, R> til en standard Function<T, R> og accepterer en anden parameter, der angiver fallback-værdien i tilfælde af en undtagelse:
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() tager en ThrowingFunction<T, R> og konverterer den til en standard Function<T, R> med håndtering af undtagelser. Hvis der opstår en fejl, logges beskeden og den angivne standardværdi returneres.
Anvendelse i Stream API
Antag, at der findes en liste over brugere i en onlinebutik, og det er nødvendigt at finde aktive brugere, som har mindst tre ordrer med en værdi på over 10.000. Hvis en ordre har et negativt beløb, skal streamen ikke stoppes—der returneres blot 0 som en indikation af, at prisen var ugyldig.
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, hvis en ordre indeholder et negativt beløb, stopper programmet ikke, men logger blot en fejl og erstatter det med 0,0. Dette gør databehandling mere robust og praktisk til virkelige anvendelser.
Tak for dine kommentarer!