Exception Handling

 

Streamlining Resource Management with try-with-resources

try-with-resources, a syntactic sugar introduced in Java 7 that significantly simplifies resource closing process. It ensures that any resource declared within its parentheses, which implements the java.lang.AutoCloseable interface, is automatically closed at the end of the try block, regardless of whether an exception occurs or not:

public class NewWayReadFile {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.err.println("Error reading file: " + e.getMessage());
}
}
}

Handling Exceptions in Java Streams

a) Wrap Checked Exceptions in Runtime Exceptions.

Cons: Obscures the original checked exception type. Requires catching RuntimeException downstream.

b) Create a Wrapper Lambda/Function.

create a helper method or a generic functional interface that encapsulates the try-catch logic, allowing you to pass it to stream operations.

// Helper method to wrap a throwing function into a non-throwing one
public static <T, R> Function<T, R> wrap(ThrowingFunction<T, R> throwingFunction) {
return t -> {
try {
return throwingFunction.apply(t);
} catch (Exception e) {
throw new RuntimeException(e); // Or log and return a default/null
}
};
}

lines.map(wrap(line -> {
if (line.contains("error")) {
throw new IOException("Simulated error processing line: " + line);
}
return line.toUpperCase();
}))
.forEach(System.out::println);

Cons: We are still wrapping into RunTime exception.

c) Use third party library, using vavr:

import io.vavr.control.Try;

List<Try<FileReader>> results = paths.stream()
.map(path -> Try.of(() -> new FileReader(path)))
.collect(Collectors.toList());

// Separate successes and failures
List<FileReader> successes = results
.filter(Try::isSuccess)
.map(Try::get)
.toList();

List<Throwable> failures = results
.filter(Try::isFailure)
.map(Try::getCause)
.toList();

or using Apache ExceptionUtils:

import org.apache.commons.lang3.exception.ExceptionUtils;

class Either<T, E extends Exception> { ... }

public class TryEitherExample {

public static Either<String, Exception> doSomething() {
try {
// Code that might throw an exception
String result = someMethodThatMightThrow();
return new Either.Right<>(result); // Success
} catch (Exception e) {
// ExceptionUtils.wrapAndThrow(e); // Not ideal for returning Either, just showing the method
return new Either.Left<>(e); // Failure
}
}

public static String someMethodThatMightThrow() throws Exception {
// Simulate an exception
if (Math.random() < 0.5) {
throw new Exception("Simulated exception");
}
return "Success!";
}

public static void main(String[] args) {
Either<String, Exception> result = doSomething();

result.fold(
success -> System.out.println("Success: " + success),
failure -> {
System.err.println("Failure: " + failure.getMessage());
// Handle the exception
}
);
}
}

d) Collect Exceptions Separately, accumulate both results and errors into a custom holder:

record Result<T>(T value, Exception error) {
}

List<Result<FileReader>> list = paths.stream()
.map(path -> {
try {
return new Result<>(new FileReader(path), null);
} catch (IOException e) {
return new Result<>(null, e);
}
})
.collect(Collectors.toList());

End