DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations

Trending

  • Reactive Programming
  • Chaining API Requests With API Gateway
  • Integrating AWS With Salesforce Using Terraform
  • Revolutionizing Algorithmic Trading: The Power of Reinforcement Learning

Trending

  • Reactive Programming
  • Chaining API Requests With API Gateway
  • Integrating AWS With Salesforce Using Terraform
  • Revolutionizing Algorithmic Trading: The Power of Reinforcement Learning

How to Handle Checked Exceptions With Lambda Expression

Check out how to handle checked exceptions in Java with this tutorial using lambda expressions for more concise code.

A N M Bazlur Rahman user avatar by
A N M Bazlur Rahman
CORE ·
Updated Nov. 07, 18 · Tutorial
Like (27)
Save
Tweet
Share
130.28K Views

Join the DZone community and get the full member experience.

Join For Free

Lambda expressions are all about how we write code. It’s about how we can write more concise, smaller, and less boilerplate code. However, this aforementioned statement may not seem to be true in the case of exceptions in Java.

In Java, we can only handle exceptions through the try-catch block, and this hasn’t changed for the lambda expression.

Let’s say we’re going to develop a simple web crawler. The crawler will take a list of URLs in a string as an argument and save the content of the text file in a text file. Let’s do this.

import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.UUID;

public class WebCrawler {
    public static void main(String[] args) {
        List<String> urlsToCrawl = List.of(“https://masterdevskills.com");

        WebCrawler webCrawler = new WebCrawler();
        webCrawler.crawl(urlsToCrawl);
    }

    public void crawl(List<String> urlsToCrawl) {
        urlsToCrawl.stream()
                .map(urlToCrawl -> new URL(urlToCrawl))
                .forEach(url -> save(url));
    }

    private void save(URL url) throws IOException {
        String uuid = UUID.randomUUID().toString();
        InputStream inputStream = url.openConnection().getInputStream();
        Files.copy(inputStream, Paths.get(uuid + ".txt"), StandardCopyOption.REPLACE_EXISTING);
    }
}


The above code is simple and intuitive. We used Stream and the first map method converts string URL to java.net.URL object and then passed it to the forEach method, which saves it in a text file. We have used a lambda expression in the crawl method. However, the above code won’t compile. The reason is that we didn’t handle the checked exceptions. The constructor of java.net.URL class throws the MalformedURLException checked exception. And, our private save method also throws checked exceptions, which is the IOException.

Let’s handle the exceptions.

public void crawl(List<String> urlsToCrawl) {
    urlsToCrawl.stream()
            .map(urlToCrawl -> {
                try {
                    return new URL(urlToCrawl);
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }
                return null;
            })
            .forEach(url -> {
                try {
                    save(url);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
}


Lambda expressions are supposed to be concise, smaller, and crisp, but none of these apply to the aforementioned code. So here, we have a problem.

Let’s rewrite the whole program and make our lambda crisp.

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

public class WebCrawler {
    public static void main(String[] args) {
        List<String> urlsToCrawl = List.of("https://masterdevskills.com");
        WebCrawler webCrawler = new WebCrawler();
        webCrawler.crawl(urlsToCrawl);
    }

    public void crawl(List<String> urlsToCrawl) {
        urlsToCrawl.stream()
                .map(this::createURL)
               .forEach(this::save);
    }

    private URL createURL(String urlToCrawl) {
        try {
            return new URL(urlToCrawl);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return null;
    }

    private void save(URL url) {
        try {
            String uuid = UUID.randomUUID().toString();
            InputStream inputStream = url.openConnection().getInputStream();
            Files.copy(inputStream, Paths.get(uuid + ".txt"), StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


Now look carefully at the crawl() method. We replaced the lambda expression with the method reference. It’s now more concise, smaller, and crisp. However, we were not able to solve the problem of handling the exception, we just moved it to a different place.

We have another problem here, we handle the exception in the method with the try-catch block in places but did not delegate the exception up the stack of the method call where we actually called the crawl() method.

We can solve this problem by re-throwing the checked exception using  RuntimeException, which will work since we don’t have to handle runtime exception if we don’t want to and our lambda expression will remain concise.

Let’s do that:

public void crawl(List<String> urlsToCrawl) {
        urlsToCrawl.stream()
                .map(this::createURL)
                .forEach(this::save);
    }

    private URL createURL(String urlToCrawl) {
        try {
            return new URL(urlToCrawl);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

    private void save(URL url) {
        try {
            String uuid = UUID.randomUUID().toString();
            InputStream inputStream = url.openConnection().getInputStream();
            Files.copy(inputStream, Paths.get(uuid + ".txt"), StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


The solution seems to work, but the amount of boilerplate code didn’t reduce. Let’s work on that now.

The map() method of stream takes a functional interface. We can write a similar functional interface, which would use a checked exception. Let’s do that.

@FunctionalInterface
public interface ThrowingFunction<T, R, E extends Throwable> {
    R apply(T t) throws E;
}


This functional interface has three generic types, including one that extends a Throwable. Since Java 8, an interface can have static methods, let’s write one here.

@FunctionalInterface
public interface ThrowingFunction<T, R, E extends Throwable> {
    R apply(T t) throws E;

    static <T, R, E extends Throwable> Function<T, R> unchecked(ThrowingFunction<T, R, E> f) {
        return t -> {
            try {
                return f.apply(t);
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        };
    }
}


The above, unchecked method takes a ThrowingFunction and handles the exception, which, in turn, throws a RuntimeException and returns a Function.

Let’s use in our lambda expression:

public void crawl(List<String> urlsToCrawl) {
        urlsToCrawl.stream()
                .map(ThrowingFunction.unchecked(urlToCrawl -> new URL(urlToCrawl)))
                .forEach(this::save);
}


In the map method, the ThrowingFunction.unchecked() handles the exception inside it and returns a Function and mapmethod that uses it. This solves no more boilerplate around, and we can easily reuse this new ThrowingFunction functional interface anywhere we want.

Now, let’s take care of the forEach method of the stream API. It takes a Consumer. Here, we can also have a new ThrowingConsumer similar to the previous one.

public interface ThrowingConsumer<T, E extends Throwable> {
    void accept(T t) throws E;

    static <T, E extends Throwable> Consumer<T> unchecked(ThrowingConsumer<T, E> consumer) {
    return (t) -> {
        try {
            consumer.accept(t);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    };
  }
}


Let’s use it.

public void crawl(List<String> urlsToCrawl) {
        urlsToCrawl.stream()
                .map(ThrowingFunction.unchecked(urlToCrawl -> new URL(urlToCrawl)))
                .forEach(ThrowingConsumer.unchecked(url -> save(url)));
}

private void save(URL url) throws IOException {
        String uuid = UUID.randomUUID().toString();
        InputStream inputStream = url.openConnection().getInputStream();
        Files.copy(inputStream, Paths.get(uuid + ".txt"), StandardCopyOption.REPLACE_EXISTING);
}


Now in our code, there is no try-catch block, no boilerplate code. We can use method reference to make it crisper.

public void crawl(List<String> urlsToCrawl) {
        urlsToCrawl.stream()
                .map(ThrowingFunction.unchecked(URL::new))
                .forEach(ThrowingConsumer.unchecked(this::save));
}


In conclusion, it’s still debatable whether or not we need a checked exception. However, plenty of software projects have been delivered without checked exceptions till date. Having said that, the decisions that developers made when the language was being created impact our way of writing code today, which we cannot ignore. Java 8 changed our way of writing code. For that, we can just ignore the debate and use the above techniques when we need to deal with checked exceptions in the lambda expression.

Happy coding!

Published at DZone with permission of A N M Bazlur Rahman, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Trending

  • Reactive Programming
  • Chaining API Requests With API Gateway
  • Integrating AWS With Salesforce Using Terraform
  • Revolutionizing Algorithmic Trading: The Power of Reinforcement Learning

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: