# Handling Exceptions in Java

I needed a cleaner way of handling exceptions in Java. After doing some research, I discovered several approaches to help make code cleaner and more concise. Here is what I found:

### 1\. **Optional and orElseThrow()** (Java 8+):

Using `Optional` along with `orElseThrow()` can handle exceptions in a more concise manner.

Example:

```java
Optional<String> optionalValue = Optional.ofNullable(getValue());
String result = optionalValue.orElseThrow(() -> new NotFoundException("Value not found"));
```

### 2\. **CompletableFuture** (Java 8+):

`CompletableFuture` allows handling exceptions in asynchronous programming scenarios using methods like `exceptionally()`.

Example:

```java
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    // Some computation that might throw an exception
});

Integer result = future.exceptionally(ex -> handleException(ex)).join();
```

### 3\. **Exception Handling with Streams** (Java 8+):

Streams allow handling exceptions within stream operations using `map()` and `filter()` along with `Optional`.

Example:

```java
List<String> values = Arrays.asList("1", "two", "3", "four");

List<Integer> integers = values.stream()
    .map(s -> {
        try {
            return Integer.parseInt(s);
        } catch (NumberFormatException e) {
            return null; // Or handle the exception here
        }
    })
    .filter(Objects::nonNull)
    .collect(Collectors.toList());
```

### 4\. **Using try-with-resources** (Java 7+):

For handling resources that need to be closed automatically, use try-with-resources to avoid explicit catch blocks.

Example:

```java
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    // Use the reader
} catch (IOException e) {
    // Handle exception
}
```

### 5\. **Functional Error Handling** (Java 8+):

Functional libraries like Vavr or functional interfaces (`Function`, `Supplier`, etc.) often offer utilities for handling exceptions in a more functional style.

Example using Vavr:

```java
Try<Integer> result = Try.of(() -> Integer.parseInt("123"));
```

As a side note, you can define custom unchecked exceptions (extending `RuntimeException`) to avoid the need for explicit catch blocks for certain exceptions. Also, favor unchecked exceptions over checked exceptions when possible to reduce boilerplate code.

### Conclusion

The choice of exception handling method depends on the specific context, readability, and maintainability. Using these modern techniques can lead to cleaner and more expressive code, but the choice should be made based on the specific needs of the application and codebase.
