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
  1. DZone
  2. Coding
  3. Java
  4. Why Try? Better Exception Handling in Java With Try

Why Try? Better Exception Handling in Java With Try

Concurrency and streams put up special challenges for exception handling, particularly checked exceptions. Fortunately, you can try to improve your lot with Try.

Reinout Korbee user avatar by
Reinout Korbee
·
Feb. 28, 17 · Tutorial
Like (15)
Save
Tweet
Share
32.40K Views

Join the DZone community and get the full member experience.

Join For Free

Exception handling in Java is difficult with streams, IO, and concurrency. A lot of people are saying that it even might be a failed experiment. Broken. I am not saying it is but that is what I've heard, just ask around. Checked exceptions are not what they used to be. They won't write about it but I will. Make the Try great again. &:0

Do or Do Not, There Is No Try

Yoda says there is no Try in Java 8. You have to write it yourself. Sad. Oracle doesn't provide a Try and throws exceptions at innocent methods — NO JVM INSTALLS? &:0

It is not that difficult, though. A Try is an object that wraps an exception so it is not immediately thrown. A Try is either a success or a failure. A developer can inspect the Try and ask if it is a success or not. According to the Scala documentation, a Try can be chained, catching exceptions along the way. A good explanation is found at: What's wrong with Java 8, part IV.

The Try Construct

There are several implementations of Try, including the basic implementation we go over here. A more elaborate implementation is found at: https://github.com/jasongoodwin/better-java-monads. The basic implementation tries a function call, wraps the exception, and returns a success or failure. The Try can be chained with the map and flatMap operations:

public abstract class Try < V > {

    private Try() {}

    public abstract Boolean isSuccess();

    public abstract Boolean isFailure();

    public abstract void throwException();

    public abstract Throwable getMessage();

    public abstract Vget();

    public abstract < U > Try < U > map(CheckedFunction << ? super V, ? extends U > f);

    public abstract < U > Try < U > flatMap(CheckedFunction << ? super V, Try < U >> f);

    public static < V > Try < V > failure(Throwable t) {
        Objects.requireNonNull(t);
        return new Failure < > (t);
    }

    public static < V > Try < V > success(V value) {
        Objects.requireNonNull(value);
        return new Success < > (value);
    }

    public static < T > Try < T > failable(CheckedSupplier < T > f) {
        Objects.requireNonNull(f);

        try {
            return Try.success(f.get());
        } catch (Throwable t) {
            return Try.failure(t);
        }
    }

    private static class Failure < V > extends Try < V > {

        private RuntimeException exception;

        public Failure(Throwable t) {
            super();
            this.exception = new RuntimeException(t);
        }

        @Override
        public Boolean isSuccess() {
            return false;
        }

        @Override
        public void throwException() {
            throw this.exception;
        }

        @Override
        public Vget() {
            throw exception;
        }

        @Override
        public Boolean isFailure() {
            return true;
        }

        @Override
        public < U > Try < U > map(CheckedFunction << ? super V, ? extends U > f) {
            Objects.requireNonNull(f);
            return Try.failure(exception);
        }

        @Override
        public < U > Try < U > flatMap(CheckedFunction << ? super V, Try < U >> f) {
            Objects.requireNonNull(f);
            return Try.failure(exception);
        }

        @Override
        public Throwable getMessage() {
            return exception;
        }
    }

    private static class Success < V > extends Try < V > {

        private final V value;

        public Success(V value) {
            super();
            this.value = value;
        }

        @Override
        public Boolean isSuccess() {
            return true;
        }

        @Override
        public void throwException() {
            return;
        }

        @Override
        public Vget() {
            return value;
        }

        @Override
        public Boolean isFailure() {
            return false;
        }

        @Override
        public < U > Try < U > map(CheckedFunction << ? super V, ? extends U > f) {
            Objects.requireNonNull(f);
            try {
                return Try.success(f.apply(value));
            } catch (Throwable t) {
                return Try.failure(t);
            }
        }

        @Override
        public < U > Try < U > flatMap(CheckedFunction << ? super V, Try < U >> f) {
            Objects.requireNonNull(f);
            try {
                return f.apply(value);
            } catch (Throwable t) {
                return Try.failure(t);
            }
        }

        @Override
        public Throwable getMessage() {
            throw new IllegalStateException("no messages when success");
        }
    }
}


A Try is an abstract class with a static initializer method, failable(), which returns either a Success or a Failure. A Success contains a value and a Failure contains a RuntimeException with the original exception as the cause. The exception is unchecked so that if it is thrown, it will halt the program and doesn't need to be caught again.

A Scala Example in Java

In Scala, an example is provided on how to use the Try, available at: http://www.scala-lang.org/api/2.9.3/scala/util/Try.html. This example takes two integers from the console and tries to divide them. If one of the strings can't be parsed into an integer, or if the divisor is zero, an exception is thrown. The Try returns a failure and that event can be handled gracefully:

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
Try<Integer> dividend = Try.failable( () -> {
        System.out.println("Enter an Int that you'd like to divide:");
        return Integer.parseInt(bufferedReader.readLine());
} );

Try<Integer> divisor = Try.failable( () -> {
        System.out.println("Enter an Int that you'd like to divide by:");
        return Integer.parseInt(bufferedReader.readLine());
});

Try<Integer> problem = dividend.<Integer>flatMap(x -> divisor.<Integer>map(y -> x/y));

if(problem.isSuccess()) {
        System.out.println("Result of " + dividend.get() + "/"+ divisor.get() +" is: " + problem.get());
} else if(problem.isFailure()) {
        System.out.println("You must've divided by zero or entered something that's not an Int. Try again!");
        System.out.println("Info from the exception: " + problem.getMessage());
}   


In this example, all functionality of the Try is clearly visible. Exceptions are chained and saved for later so that they can be handled gracefully in code.

Try Eith Streams

Streams can't handle checked exceptions. Throwing exceptions from within the stream will break the type system. Wrapping exceptions is perfectly all right. See Brian Goetz's answer on Stackoverflow: How can I throw checked exceptions from inside Java 8 streams. The Try wraps the checked exception, but doesn't throw it. Instead, it provides the option to inspect the result and decide to either handle the failure or throw an unchecked exception. This is also legal in streams. For example:

List<Integer> results = Stream.iterate(0, i -> i + 1)
                .<Try<Integer>>map(i -> {
                        return Try.failable( () -> checkedExceptionThrowingMethod(i) );
                })
                .filter(t -> t.isSuccess())
                .map(t -> t.get())
                .collect(Collectors.toList());


In the above example, a method which may throw a checked exception, is called in the stream. The compiler will flag this, but when wrapped in a Try, the code is executed and the eventual exceptions are stored in the Try. With the filter method, all failures are filtered out and we get a list of results. It is easy to see how success and failure can be handled. The results can be passed to a different location for further inspection.

Try With Future

Concurrency is another area where exception handling can be difficult. With the Java Executor, you get a Future, which may contain an exception. When calling the get method, the exception is immediately thrown. You don't know beforehand whether the result is available or not. Maybe you don't want to throw and catch the exception. Maybe you're only interested in those Futures that did finish successfully. Either way, the following example shows how the result of a Future is stored in a Try and made available for inspection:

ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

List < Integer > ints = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
List < Try < Integer >> results = new ArrayList < > ();
List < Future < Integer >> futures = new ArrayList < > ();
for (Integer i: ints) {
    int n = i % 3;
    Callable < Integer > task = () - > {
        return 1 / n;
    };
    Future < Integer > f = service.submit(task);
    futures.add(f);
}

for (Future < Integer > f: futures) {
    results.add(Try.failable(() - > f.get()));
}

int failures = 0;
for (Try < Integer > t: results) {
    if (t.isSuccess()) {
        System.out.println("result: " + t.get());
    } else {
        failures++;
    }
}
System.out.println(failures + " failures");


No try-catch blocks needed and the code still handles exceptions gracefully.

Why Not Try Try?

In the previous examples, we saw that the Try is useful in several scenarios, and there are more. It is a good replacement for Optional, for example. If Try is so useful, why is it not part of the Java JDK? The Future and the Optional would be easier if they had Try semantics. In streams, a Try is useful when dealing with checked exceptions to wrap them in unchecked exceptions and still be able to handle the exceptions gracefully. I suggest Oracle take the Try seriously, but not literally. &:0

Java (programming language)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Is DevOps Dead?
  • What Is API-First?
  • Key Elements of Site Reliability Engineering (SRE)
  • OWASP Kubernetes Top 10

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
  • +1 (919) 678-0300

Let's be friends: