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
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

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
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

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Java Code Review Solution
  • Leverage Lambdas for Cleaner Code
  • Getting Started With JMS-ActiveMQ: Explained in a Simple Way
  • Binary Code Verification in Open Source World

Trending

  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes
  • Unit Testing Large Codebases: Principles, Practices, and C++ Examples
  • Designing a Java Connector for Software Integrations
  • The Full-Stack Developer's Blind Spot: Why Data Cleansing Shouldn't Be an Afterthought
  1. DZone
  2. Coding
  3. Java
  4. How to Handle Checked Exceptions With Lambda Expression

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.

By 
A N M Bazlur Rahman user avatar
A N M Bazlur Rahman
DZone Core CORE ·
Updated Nov. 07, 18 · Tutorial
Likes (27)
Comment
Save
Tweet
Share
138.6K 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!

Java (programming language) code style

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.

Related

  • Java Code Review Solution
  • Leverage Lambdas for Cleaner Code
  • Getting Started With JMS-ActiveMQ: Explained in a Simple Way
  • Binary Code Verification in Open Source World

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

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

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!