java 中对异常进行忽略_为什么您应该忽略Java中的异常以及如何正确进行处理

java 中对异常进行忽略

by Rainer Hahnekamp

通过Rainer Hahnekamp

为什么您应该忽略Java中的异常以及如何正确进行处理 (Why you should ignore exceptions in Java and how to do it correctly)

In this article, I will show how to ignore checked exceptions in Java. I will start by describing the rationale behind it and the common pattern to resolve this issue. Then I will present some libraries for that purpose.

在本文中,我将展示如何忽略Java中的检查异常。 我将首先描述其背后的原理以及解决此问题的通用模式。 然后,我将为此目的提供一些库。

已检查和未检查的异常 (Checked and Unchecked Exceptions)

In Java, a method can force its caller to deal with the occurrence of potential exceptions. The caller can use the try/catch clause, where the try contains the actual code and catch contains the code to execute when the exception occurs.

在Java中,方法可以强制其调用方处理潜在异常的发生。 调用者可以使用try / catch子句,其中try包含实际代码,而catch包含在发生异常时执行的代码。

Alternatively, the caller can pass on that burden to its parent caller. This can go upwards until the main method is reached. If the main method also passes on the exception, the application will crash when an exception happens.

替代地,呼叫者可以将负担转嫁给其父呼叫者 。 这可以向上进行,直到达到主要方法为止。 如果main方法也传递了异常,则发生异常时应用程序将崩溃。

In the case of an exception, there are many scenarios where the application cannot continue to run and needs to stop. There are no alternative paths. Unfortunately, that means Java forces us to write code for a situation where the application shouldn’t run anymore. Quite useless!

在例外情况下,在许多情况下应用程序无法继续运行而需要停止。 没有其他选择。 不幸的是,这意味着Java迫使我们为应用程序不再运行的情况编写代码。 没用!

An option is to minimise that boilerplate code. We can wrap the exception into a RuntimeException, which is an unchecked exception. This has the effect that, even though the application still crashes, we don’t have to provide any handling code.

一种选择是最小化该样板代码。 我们可以将异常包装到RuntimeException中 ,这是未经检查的异常。 这样的结果是,即使应用程序仍然崩溃,我们也不必提供任何处理代码。

By no means do we log the exception and let the application continue like nothing has happened. It is possible, but is similar to opening Pandora’s Box.

我们绝不记录异常,并让应用程序像没有发生一样继续运行。 可能,但是类似于打开Pandora的盒子。

We call these exceptions, for which we have to write extra code, checked exceptions. The others of the RuntimeException type we call unchecked exceptions.

我们将这些异常称为必须检查的异常 ,为此我们必须编写额外的代码 其他RuntimeException类型的我们称为未检查的异常

为什么要检查异常? (Why Checked Exceptions at all?)

We can find lots of checked exceptions in third-party libraries, and even in the Java Class Library itself. The reason is pretty straightforward. A library vendor cannot predict in which context the developer will use their code.

我们可以在第三方库中甚至在Java类库本身中找到很多经过检查的异常。 原因很简单。 库供应商无法预测开发人员将在哪种上下文中使用其代码。

Logically, they don’t know if our application has alternative paths. So they leave the decision to us. Their responsibility is to “label” methods that can potentially throw exceptions. Those labels give us the chance to implement counter-actions.

从逻辑上讲,他们不知道我们的应用程序是否具有替代路径。 因此,他们将决定权留给了我们。 他们的责任是“标记”可能引发异常的方法。 这些标签使我们有机会实施反措施。

A good example is the connection to a database. The library vendor marks the connection retrieval method with an exception. If we use the database as a cache, we can send our queries directly to our primary database. This is the alternative path.

一个很好的例子是与数据库的连接。 库供应商将连接检索方法标记为异常。 如果我们使用数据库作为缓存,则可以将查询直接发送到主数据库。 这是替代路径。

If our database is not the cache, there is no way the application can continue to run. And it’s OK if the application crashes:

如果我们的数据库不是缓存,则应用程序无法继续运行。 如果应用程序崩溃,也可以:

丢失的数据库 (A lost database)

Let’s put our theoretical example to real code:

让我们将理论示例应用于实际代码:

public DbConnection getDbConnection(String username, String password) {  try {    return new DbProvider().getConnection(username, password);  } catch (DbConnectionException dce) {    throw new RuntimeException(dce);  }}

The database is not used as a cache. In the event of a lost connection, we need to stop the application at once.

该数据库不用作缓存。 如果连接丢失,我们需要立即停止应用程序。

As described above, we wrap the DbConnectionException into a RuntimeException.

如上所述,我们将DbConnectionException包装到RuntimeException中

The required code is relatively verbose and always the same. This creates lots of duplication and decreases the readability.

所需的代码相对冗长且始终相同。 这样会造成大量重复,并降低了可读性。

RuntimeException包装器 (The RuntimeException Wrapper)

We can write a function to simplify this. It should wrap a RuntimeException over some code and return the value. We cannot simply pass code in Java. The function must be part of a class or interface. Something like this:

我们可以编写一个函数来简化此过程。 它应该在一些代码上包装一个RuntimeException并返回该值。 我们不能简单地用Java传递代码。 该函数必须是类或接口的一部分。 像这样:

public interface RuntimeExceptionWrappable<T> {  T execute() throws Exception;} public class RuntimeExceptionWrapper {  public static <T> T wrap(RuntimeExceptionWrappable<T> runtimeExceptionWrappable) {    try {      return runtimeExceptionWrappable.execute();    } catch (Exception exception) {      throw new RuntimeException(exception);    }  }} public class DbConnectionRetrieverJava7 {  public DbConnection getDbConnection(final String username, final String password) {    RuntimeExceptionWrappable<DbConnection> wrappable = new RuntimeExceptionWrappable<DbConnection>() {      public DbConnection execute() throws Exception {        return new DbProvider().getConnection(username, password);      }    };    return RuntimeExceptionWrapper.wrap(wrappable);  }}

The RuntimeException wrapping has been extracted into its own class. In terms of software design, this might be the more elegant solution. Still, given the amount of code, we can hardly say the situation got better.

RuntimeException包装已提取到其自己的类中。 在软件设计方面,这可能是更优雅的解决方案。 尽管如此,鉴于代码量,我们很难说情况会好转。

With Java 8 lambdas, things got easier. If we have an interface with one method only, then we just write the specific code of that method. The compiler does the rest for us. The unnecessary or “syntactic sugar code” to create a specific or anonymous class is not required any more. That’s the basic use case for Lambdas.

使用Java 8 lambda,事情变得更加容易。 如果我们只有一个方法的接口,那么我们只需编写该方法的特定代码。 编译器为我们完成其余的工作。 不再需要创建特定类或匿名类的不必要的或“语法糖代码”。 这是Lambda的基本用例。

In Java 8, our example above looks like:

在Java 8中,上面的示例如下所示:

@FunctionalInterfacepublic interface RuntimeExceptionWrappable<T> {  T execute() throws Exception;} public class DbConnectionRetrieverJava8 {  public DbConnection getDbConnection(String username, String password) {    return RuntimeExceptionWrapper.wrap(() ->      new DbProvider().getConnection(username, password));  }}

The difference is quite clear: the code is more concise.

区别很明显:代码更加简洁。

Streams&Co.中的例外 (Exceptions in Streams & Co.)

RuntimeExceptionWrappable is a very generic interface. It is just a function that returns a value. Use cases for that function, or its variations, appear all over. For our convenience, Java’s Class Library has a set of such common Interfaces built-in. They are in the package java.util.function and are better known as the “Functional Interfaces.” Our RuntimeExceptionWrappable is similar to java.util.function.Supplier<;T>.

RuntimeExceptionWrappable是一个非常通用的接口。 它只是一个返回值的函数。 该功能或其变型的用例随处可见。 为方便起见,Java的类库内置了一组此类通用​​接口。 它们位于java.util.functionjava.util.function并且被称为“功能接口”。 我们的RuntimeExceptionWrappablejava.util.function.Supplier< ; T>相似。

These interfaces form the prerequisite of the powerful Stream, Optional, and other features which are also part of Java 8. In particular, Stream comes with a lot of different methods for processing collections. Many of these methods have a “Functional Interface” as parameter.

这些接口构成了强大的Stream,Optional和Java 8的其他功能的先决条件。特别是,Stream附带了许多用于处理集合的不同方法。 这些方法中的许多方法都有“功能接口”作为参数。

Let’s quickly switch the use case. We have a list of URL strings that we want to map into a list of objects of type java.net.URL.

让我们快速切换用例。 我们有一个URL字符串列表,我们希望将其映射到java.net.URL类型的对象列表中。

The following code does not compile:

以下代码无法编译

public List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com", “https://www.austria.info")    .map(this::createURL)    .collect(Collectors.toList());} private URL createURL(String url) throws MalformedURLException {  return new URL(url);}

There is a big problem when it comes to exceptions. The Interfaces defined in java.util.function don’t throw exceptions. That’s why our method createURL doesn’t have the same signature as java.util.function.Function, which is the parameter of the map method.

当涉及到异常时,存在一个大问题。 java.util.function定义的接口不会引发异常。 这就是为什么我们的方法createURLjava.util.function.Function具有不同的签名的原因,后者是map方法的参数。

What we can do is to write the try/catch block inside the lambda:

我们可以做的是在lambda里面写try / catch块:

public List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com", “https://www.austria.info")    .map(url -> {      try {        return this.createURL(url);      } catch (MalformedURLException e) {        throw new RuntimeException(e);      }    })    .collect(Collectors.toList());}

This compiles, but doesn’t look nice either. We can now take a step further and write a wrapper function along a new interface similar to RuntimeExceptionWrappable`:

这样可以编译,但是看起来也不好。 现在,我们可以更进一步,并沿着类似于RuntimeExceptionWrappable`的新接口编写包装器函数:

@FunctionalInterfacepublic interface RuntimeWrappableFunction<T, R> {  R apply(T t) throws Exception;} public class RuntimeWrappableFunctionMapper {  public static <T, R> Function<T, R> wrap(    RuntimeWrappableFunction<T, R> wrappable) {      return t -> {        try {          return wrappable.apply(t);        } catch(Exception exception) {          throw new RuntimeException(exception);        }      };    }}

And apply it to our Stream example:

并将其应用于我们的Stream示例:

public List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com”, “https://www.austria.info”)    .map(RuntimeWrappableFunctionMapper.wrap(this::createURL))    .collect(Collectors.toList());} private URL createURL(String url) throws MalformedURLException {  return new URL(url);}

Great! Now we have a solution, where we can:

大! 现在我们有了一个解决方案,在这里我们可以:

  • run code without catching checked exceptions, and

    运行代码而不捕获检查的异常,并且
  • use exception-throwing lambdas in Stream, Optional, and so on.

    在Stream,Optional等中使用抛出异常的Lambda。

偷偷抢救 (SneakyThrow to the rescue)

The SneakyThrow library lets you skip copying and pasting the code snippets from above. Full disclosure: I am the author.

SneakyThrow库使您可以从上方跳过复制和粘贴代码段。 完全公开:我是作者。

SneakyThrow comes with two static methods. One runs code without catching checked exceptions. The other method wraps an exception-throwing lambda into one of the Functional Interfaces:

SneakyThrow带有两种静态方法。 一个运行代码而不捕获检查的异常。 另一种方法将引发异常的lambda包装到一个功能接口中:

//SneakyThrow returning a resultpublic DbConnection getDbConnection(String username, String password) {  return sneak(() -> new DbProvider().getConnection(username, password));} //SneakyThrow wrapping a functionpublic List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com", “https://www.austria.info")    .map(sneaked(this::createURL))    .collect(Collectors.toList());}

替代图书馆 (Alternative Libraries)

投掷功能 (ThrowingFunction)
//ThrowingFunction returning a resultpublic DbConnection getDbConnection(String username, String password) {  return unchecked(() ->     new DbProvider().getConnection(username, password)).get();} //ThrowingFunction returning a functionpublic List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com", “https://www.austria.info")    .map(unchecked(this::createURL))    .collect(Collectors.toList());}

In contrast to SneakyThrow, ThrowingFunction can’t execute code directly. Instead, we have to wrap it into a Supplier and call the Supplier afterwards. This approach can be more verbose than SneakyThrow.

与SneakyThrow相比,ThrowingFunction无法直接执行代码。 相反,我们必须将其包装到供应商中,然后再致电供应商。 这种方法比SneakyThrow更为冗长。

If you have multiple unchecked Functional Interfaces in one class, then you have to write the full class name with each static method. This is because unchecked does not work with method overloading.

如果在一个类中有多个未经检查的功能接口,则必须使用每个静态方法编写完整的类名。 这是因为未选中不适用于方法重载。

On the other hand, ThrowingFunction provides you with more features than SneakyThrow. You can define a specific exception you want to wrap. It is also possible that your function returns an Optional, otherwise known as “lifting.”

另一方面,ThrowingFunction比SneakyThrow提供更多的功能。 您可以定义要包装的特定异常。 您的函数还可能返回一个Optional,也称为“ lifting”。

I designed SneakyThrow as an opinionated wrapper of ThrowingFunction.

我将SneakyThrow设计为ThrowingFunction的自包装。

瓦夫 (Vavr)

Vavr, or “JavaSlang,” is another alternative. In contrast to SneakyThrow and ThrowingFunction, it provides a complete battery of useful features that enhance Java’s functionality.

Vavr或“ JavaSlang”是另一种选择。 与SneakyThrow和ThrowingFunction相比,它提供了一整套有用的功能,这些功能增强了Java的功能。

For example, it comes with pattern matching, tuples, its own Stream and much more. If you haven’t heard of it, it is definitely worth a look. Prepare to invest some time in order to understand its full potential.

例如,它带有模式匹配,元组,自己的Stream等。 如果您还没有听说过,那绝对值得一看。 准备投入一些时间以了解其全部潜力。

//Vavr returning a resultpublic DbConnection getDbConnection(String username, String password) {  return Try.of(() ->   new DbProvider().getConnection(username, password))    .get();} //Vavr returning a functionpublic List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com", “https://www.austria.info")    .map(url -> Try.of(() -> this.createURL(url)).get())    .collect(Collectors.toList());}
Lombok计划 (Project Lombok)

Such a list of libraries is not complete without mentioning Lombok. Like Vavr, it offers much more functionality than just wrapping checked exceptions. It is a code generator for boilerplate code and creates full Java Beans, Builder objects, logger instances, and much more.

如果不提及Lombok,这样的库列表是不完整的。 像Vavr一样,它提供的功能远远超过仅包装已检查的异常。 它是用于样板代码的代码生成器,并创建完整的Java Bean,Builder对象,记录器实例等。

Lombok achieves its goals by bytecode manipulation. Therefore, we require an additional plugin in our IDE.

Lombok通过字节码操作实现其目标。 因此,我们在IDE中需要一个附加插件。

@SneakyThrows is Lombok’s annotation for manipulating a function with a checked exception into one that doesn’t. This approach doesn’t depend on the usage of lambdas, so you can use it for all cases. It is the least verbose library.

@SneakyThrows是Lombok的注释,用于将具有已检查异常的函数处理为没有的函数。 这种方法不依赖于lambda的用法,因此您可以在所有情况下使用它。 这是最不详细的库。

Please keep in mind that Lombok manipulates the bytecode which can cause problems with other tooling you might use.

请记住,Lombok会操纵字节码,这可能会导致您可能使用的其他工具出现问题。

//Lombok returning a result@SneakyThrowspublic DbConnection getDbConnection(String username, String password) {  return new DbProvider().getConnection(username, password);} //Lombok returning a functionpublic List<URL> getURLs() {  return Stream    .of(“https://www.hahnekamp.com", “https://www.austria.info")    .map(this::createURL)    .collect(Collectors.toList());} @SneakyThrowsprivate URL createURL(String url) {  return new URL(url);}
进一步阅读 (Further Reading)

The code is available on GitHub

该代码可在GitHub上获得

Originally published at www.rainerhahnekamp.com on March 17, 2018.

最初于2018年3月17日发布在www.rainerhahnekamp.com上。

翻译自: https://www.freecodecamp.org/news/why-you-should-ignore-exceptions-in-java-and-how-to-do-it-correctly-8e95e5775e58/

java 中对异常进行忽略

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值