Exception recovery in java

Обработка исключений в Java в функциональном стиле

В данной статье автор предоставит информацию о собственной библиотеке для обработки исключений (Exception) в функциональном стиле.

Предпосылки

В Java начиная с версии 8 появились новые возможности в виде функциональных интерфейсов и потоков (Stream API). Эти возможности позволяют писать код в новом функциональном стиле без явных циклов, временных переменных, условий ветвления и проч. Я уверен что этот стиль программирования станет со временем основным для большинства Java программистов.

Однако применение функционального стиля на практике осложняется тем, что все стандартные функциональные интерфейсы из пакета java.util.function не объявляют проверяемых исключений (являются checked exception unaware).

Рассмотрим простой пример преобразования URL из строкового представления к объектам URL.

 public List urlListTraditional(String[] urls) < return Stream.of(urls) .map(URL::new) //MalformedURLException here .collect(Collectors.toList()); >

К сожалению данный код не будет компилироваться из-за того, что конструктор URL может выбросить MalformedURLException . Правильный код будет выглядеть следующим образом

 public List urlListTraditional(String[] urls) < return Stream.of(urls) .map(s -> < try < return new URL(s); >catch (MalformedURLException me) < return null; >>).filter(Objects::nonNull) .collect(Collectors.toList()); > 

Мы должны явно обработать MalformedURLException , вернуть null из лямбды, а затем отфильтровать нулевые значения в потоке. Увы, такой код на практике нивелирует все преимущества функционального подхода.

При использовании функционального подхода к обработке исключений, код может выглядеть гораздо приятней.

 public List urlListWithTry(String[] urls) < return Stream.of(urls) .map(s ->Try.of(() -> new URL(s))) .flatMap(Try::stream) .collect(Collectors.toList()); > 

Интерфейс Try

Интерфейс Try представляет собой некоторое вычисление, которое может завершиться успешно с результатом типа T или неуспешно с исключением. Try очень похож на Java Optional , который может иметь результат типа T или не иметь результата вообще (иметь null значение).

Объекты Try создаются с помощью статического фабричного метода Try.of(. ) который принимает параметром поставщика (supplier) значения типа T, который может выбросить любое исключение Exception.

 Try url = Try.of(() -> new URL("foo")); 

Каждый объект Try находится в одном из двух состояний — успеха или неудачи, что можно узнать вызывая методы Try#isSuccess() или Try#isFailure() .

Для логирования исключений подойдет метод Try#onFailure(Consumer) , для обработки успешных значений — Try#.onSuccess(Consumer) .

Многие методы Try возвращают также объект Try, что позволяет соединять вызовы методов через точку (method chaining). Вот пример как можно открыть InputStream от строкового представления URL в несколько строк без явного использования try/catch .

 Optional input = Try.success(urlString) //Factory method to create success Try from value .filter(Objects::nonNull) //Filter null strings .map(URL::new) //Creating URL from string, may throw an Exception .map(URL::openStream) //Open URL input stream, , may throw an Exception .onFailure(e -> logError(e)) //Log possible error .optional(); //Convert to Java Optional 

Интеграция Try с Java Optional и Stream

Try легко превращается в Optional при помощи метода Try#optional() , так что в случае неуспешного Try вернется Optional.empty .

Я намеренно сделал Try API восьма похожим на Java Optional API. Методы Try#filter(Predicate) и Try#map(Function) имеют аналогичную семантику соответствующих методов из Optional. Так что если Вы знакомы с Optional , то Вы легко будете работать с Try .

Читайте также:  Тег INPUT, атрибут value

Try легко превращается в Stream при помощи метода Try#stream() точно так же, как это сделано для Optional#stream() . Успешный Try превращается в поток (stream) из одного элемента типа T, неуспешный Try — в пустой поток.

Фильтровать успешные попытки в потоке можно двумя способами — первый традиционный с использованием Try#filter()

 . .filter(Try::isSuccess) .map(Try::get) . 

Второй короче — при помощи Try#stream()

будет фильтровать в потоке неуспешные попытки и возвращать поток успешных значений.

Восстановление после сбоев (Recovering from failures)

Try имеет встроенные средства recover(. ) для восстановления после сбоев если вы имеете несколько стратегий для получения результата T. Предположим у Вас есть несколько стратегий:

 public T planA(); public T planB(); public T planC(); 

Задействовать все три стратегии/плана одновременно в коде можно следующим образом

 Try.of(this::planA) .recover(this::planB) .recover(this::planC) .onFailure(. ) .map(. ) . 

В данном случае сработает только первый успешный план (или ни один из них). Например, если план А не сработал, но сработал план Б, то план С не будет выполняться.

Работа с ресурсами (Try with resources)

Try имплементирует AutoCloseable интерфейс, а следовательно Try можно использовать внутри try-with-resource блока. Допустим нам надо открыть сокет, записать несколько байт в выходной поток сокета и затем закрыть сокет. Соответствующий код с использованием Try будет выглядеть следующим образом.

 try (var s = Try.of(() -> new Socket("host", 8888))) < s.map(Socket::getOutputStream) .onSuccess(out ->out.write(new byte[] )) .onFailure(e -> System.out.println(e)); > 

Сокет будет закрыт при выходе за последнюю фигурную скобку.

Выводы

Try позволяет обрабатывать исключения в функциональном стиле без явного использования конструкций try/catch/finally и поможет сделать Ваш код более коротким, выразительным, легким для понимания и сопровождения.

Надеюсь Вы получите удовольствие от использования Try

Ссылки

Автор — Сергей А. Копылов
e-mail skopylov@gmail.com

Последнее место работы
Communications Solutions CMS IUM R&D Lab
Hewlett Packard Enterprise
Ведущий специалист

Источник

How to Handle Checked & Unchecked Exceptions in Java

How to Handle Checked & Unchecked Exceptions in Java

In broad terms, a checked exception (also called a logical exception) in Java is something that has gone wrong in your code and is potentially recoverable. For example, if there’s a client error when calling another API, we could retry from that exception and see if the API is back up and running the second time. A checked exception is caught at compile time so if something throws a checked exception the compiler will enforce that you handle it.

Types of Exceptions in Java, Checked vs Unchecked

Checked Exception Examples

The code below shows the FileInputStream method from the java.io package with a red line underneath. The red line is because this method throws a checked exception and the compiler is forcing us to handle it. You can do this in one of two ways.

import java.io.File; import java.io.FileInputStream;   public class CheckedException < public void readFile() < String fileName = "file does not exist"; File file = new File(fileName); FileInputStream stream = new FileInputStream(file); > >

Try Catch

You simply wrap the Java code which throws the checked exception within a try catch block. This now allows you to process and deal with the exception. With this approach it’s very easy to swallow the exception and then carry on like nothing happened. Later in the code when what the method was doing is required you may find yourself with our good friend the NullPointerException .

We have now caught the exception and processed the error in a meaningful way by adding our code to the catch block, the code sequence carries on crisis averted.

import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException;   public class CheckedException < public void readFile() < String fileName = "file does not exist"; File file = new File(fileName); try < FileInputStream stream = new FileInputStream(file); >catch (FileNotFoundException e) < e.printStackTrace(); >> >

Throws

We use the keyword throws to throw the checked exception up the stack to the calling method to handle. This is what FileInputStream has just done to you. This looks and feels great — no messy exception code we are writing and we no longer need to handle this exception as someone else can deal with it. The calling method then needs to do something with it . maybe throw again.

Читайте также:  Image upload in php with ajax

As with try catch be wary of always throwing as you need to think who SHOULD be handling the error and what piece of code is best placed to handle it correctly.

import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException;   public class CheckedException < public void readFile() throws FileNotFoundException < String fileName = "file does not exist"; File file = new File(fileName); FileInputStream stream = new FileInputStream(file); >>

Unchecked Exceptions in Java

An unchecked exception (also known as an runtime exception) in Java is something that has gone wrong with the program and is unrecoverable. Just because this is not a compile time exception, meaning you do not need to handle it, that does not mean you don’t need to be concerned about it.

The most common Java unchecked exception is the good old NullPointerException which is when you are trying to access a variable or object that doesn’t exist.

So to summarize; the difference between a checked and unchecked exception is that a checked exception is caught at compile time whereas a runtime or unchecked exception is, as it states, at runtime. A checked exception must be handled either by re-throwing or with a try catch block, a runtime isn’t required to be handled. An unchecked exception is a programming error and are fatal, whereas a checked exception is an exception condition within your codes logic and can be recovered or retried from.

Unchecked Exception Examples

BindException

Because we live in a world where systems are built from lots of small micro services doing their own thing all talking to each other, generally over HTTP, this exception is popping up more and more. There isn’t a lot you can do about it other than find a free port. Only one system can use a single port at any one time and it’s on a first come, first serve basis. Most web applications default to port 8080 so the easiest option is to pick another one.

IndexOutOfBoundsException

This is a very common Java unchecked exception when dealing with arrays. This is telling you; you have tried to access an index in an array that does not exist. If an array has 10 items and you ask for item 11 you will get this exception for your efforts.

import java.util.ArrayList; import java.util.List;   public class IndexOutOfBounds < public static void main(String[] args) < Listlst = new ArrayList<>(); lst.add("item-1"); lst.add("item-2"); lst.add("item-3"); var result = lst.get(lst.size()); > >

The above piece of Java code is a common way to get an IndexOutOfBoundsException . The reason this trips people up is because the size of the array is 3 — makes sense; there are 3 items — but arrays are 0-based so the last item in the array is at index 2. To access the last item, it is always the size -1.

var result = lst.get(lst.size()-1);

Checked Exceptions During Runtime

Below is an example that is very commonly used in micro service architecture. If we received a request and we cannot, say, read data from our database needed for this request, the database will throw us a checked exception, maybe an SQLException or something similar. Because this data is important, we cannot fulfil this request without it.

Читайте также:  Какие html коды у символов

This means there is nothing we can actually do with this exception that can fix the problem, but if we do nothing the code will carry on its execution regardless.

We could throw the exception to the calling code until we get to the top of the chain and return the exception to the user. By doing that we are then littering all the layers above with an exception that they really do not care about, nor should they. What we really want is an unchecked exception to terminate this request gracefully.

import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException;   public class CheckedException < public void readFile() < String fileName = "file does not exist"; File file = new File(fileName); try < FileInputStream stream = new FileInputStream(file); >catch (FileNotFoundException e) < throw new ProcessingException("Error opening file"); >> > >

Above we have our same piece of Java code for handling the checked exception thrown from the FileInputStream method but this time we are throwing our own RuntimeException and because this exception isn’t checked at compile time, we don’t need to declare it.

public class ProcessingException extends RuntimeException < public ProcessingException(String message) < super(message); >>

Declaring your own exception type is as simple as extending the runtime exception class because as we have seen from the diagram at the top, RuntimeException is a subtype of Exception.

Difference Between Checked and Unchecked Exceptions in Java

To summarize, the difference between a checked and unchecked exception is:

  • A checked exception is caught at compile time whereas a runtime or unchecked exception is, as it states, at runtime.
  • A checked exception must be handled either by re-throwing or with a try catch block, whereas an unchecked isn’t required to be handled.
  • A runtime exception is a programming error and is fatal whereas a checked exception is an exception condition within your code’s logic and can be recovered or re-tried from.

Track, Analyze and Manage Errors With Rollbar

Managing errors and exceptions in your code is challenging. It can make deploying production code an unnerving experience. Being able to track, analyze, and manage errors in real-time can help you to proceed with more confidence. Rollbar automates error monitoring and triaging, making fixing Java errors easier than ever. Sign Up Today!

5 ways to reduce noise when logging your JavaScript exceptions
Can Constructors Throw Exceptions in Java

«Rollbar allows us to go from alerting to impact analysis and resolution in a matter of minutes. Without it we would be flying blind.»

Источник

Оцените статью