Sneaky throws java что это
Перейти к содержимому

Sneaky throws java что это

  • автор:

«Подлые броски» на Java

В Java концепция мгновенного броска позволяет нам генерировать любое проверенное исключение, не определяя его явно в сигнатуре метода. Это позволяет опустить объявление throws , эффективно имитируя характеристики исключения времени выполнения.

В этой статье мы увидим, как это делается на практике, рассмотрев несколько примеров кода.

2. О скрытых бросках

Проверенные исключения являются частью Java, а не JVM. В байт-коде мы можем кидать любое исключение откуда угодно, без ограничений.

В Java 8 появилось новое правило вывода типов, которое гласит, что throws T выводится как RuntimeException всякий раз, когда это разрешено. Это дает возможность реализовать скрытые броски без вспомогательного метода.

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

3. Подлые броски в действии

Как мы уже упоминали, компилятор и среда выполнения Jave могут видеть разные вещи:

Компилятор видит сигнатуру с бросками T , относящимися к типу RuntimeException , поэтому он разрешает распространение непроверенного исключения. Среда выполнения Java не видит никакого типа в бросках, поскольку все броски одинаковы: простой бросок e .

Этот быстрый тест демонстрирует сценарий:

Кроме того, можно сгенерировать проверенное исключение, используя манипуляции с байт-кодом или Thread.stop(Throwable) , но это запутанно и не рекомендуется.

4. Использование аннотаций Ломбока

Аннотация @SneakyThrows от Lombok позволяет вам создавать проверенные исключения без использования объявления throws . Это удобно, когда вам нужно вызвать исключение из метода в очень ограниченных интерфейсах, таких как Runnable.

Скажем, мы выбрасываем исключение из Runnable ; он будет передан только обработчику необработанных исключений потока .

Этот код вызовет экземпляр Exception , поэтому вам не нужно оборачивать его в RuntimeException:

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

Теперь давайте вызовем throwSneakyIOExceptionUsingLombok и ожидаем, что Lombok выдаст исключение IOException:

5. Вывод

Как мы видели в этой статье, мы можем обмануть компилятор Java, чтобы обрабатывать проверенные исключения как непроверенные.

How Lombok @SneakyThrows is hacking Java!

@SneakyThrows annotation behind the scenes for Java developers

Dhib Dhafer

If you are a Java developer and you never used Lombok (even for demo projects !) then you should quickly refer to the documentation and start using it.

Why this annotation ?

Dealing with checked exceptions could be in two ways:
— Directly catch the exception
— Or, add it the to the method signature to catch it later

Sometimes we don’t want to do what is listed above. However, we want to throw the checked exception but without declaring it in the method signature.

For example: Implementing an interface that does not allow throwing exceptions

That’s what happens when we use the @SneakyThrows annotation

@SneakyThrows can be used to sneakily throw checked exceptions without actually declaring this in your method's throws clause

How it works ?

Let’s try to create this Main class to check what is going under the hood.

What we are doing in the reality is creating a method that throws an exception IOException, which is a checked exception, and then calling the method in the main (KISS principle).

So let’s have a look on the generated code !

In the compilation phase, thanks to annotation processing, Lombok is transforming the method annotated with @SneakyThrows(IOException.class) and surrounding the method with a try catch block !

You can say: OH! this is bullshit ! I can do it myself since the beginning and catch my exception alone!

Here is the trick ! Have a close look the catch block.
It is actually re-throwing the exception caught in the try !
If you type the same generated method, the compilation would fail because it is an unhandled exception.

However, the generated code can’t be compiled again! We have bytecode that represents code that wouldn’t actually be compilable.

Conclusion

Lombok is generating code (try-catch block) that bypasses the compilation and not compilable.
Yes, @SneakyThrows fakes out the compiler.

Don’t forget to use a general catch for Exception, down the call stack, to catch the invisible checked exception.

@SneakyThrows

To boldly throw checked exceptions where no one has thrown them before!

Overview

@SneakyThrows can be used to sneakily throw checked exceptions without actually declaring this in your method’s throws clause. This somewhat contentious ability should be used carefully, of course. The code generated by lombok will not ignore, wrap, replace, or otherwise modify the thrown checked exception; it simply fakes out the compiler. On the JVM (class file) level, all exceptions, checked or not, can be thrown regardless of the throws clause of your methods, which is why this works.

  • A needlessly strict interface, such as Runnable — whatever exception propagates out of your run() method, checked or not, it will be passed to the Thread ‘s unhandled exception handler. Catching a checked exception and wrapping it in some sort of RuntimeException is only obscuring the real cause of the issue.
  • An ‘impossible’ exception. For example, new String(someByteArray, «UTF-8»); declares that it can throw an UnsupportedEncodingException but according to the JVM specification, UTF-8 must always be available. An UnsupportedEncodingException here is about as likely as a ClassNotFoundError when you use a String object, and you don’t catch those either!

Being constrained by needlessly strict interfaces is particularly common when using lambda syntax ( arg -> action ); however, lambdas cannot be annotated, which means it is not so easy to use @SneakyThrows in combination with lambdas.

Be aware that it is impossible to catch sneakily thrown checked types directly, as javac will not let you write a catch block for an exception type that no method call in the try body declares as thrown. This problem is not relevant in either of the use cases listed above, so let this serve as a warning that you should not use the @SneakyThrows mechanism without some deliberation!

You can pass any number of exceptions to the @SneakyThrows annotation. If you pass no exceptions, you may throw any exception sneakily.

With Lombok

import lombok.SneakyThrows;

public class SneakyThrowsExample implements Runnable <
@SneakyThrows ( UnsupportedEncodingException. class )
public String utf8ToString ( byte [] bytes ) <
return new String ( bytes, "UTF-8" ) ;
>

@SneakyThrows
public void run () <
throw new Throwable () ;
>
>

Vanilla Java

import lombok.Lombok;

public class SneakyThrowsExample implements Runnable <
public String utf8ToString ( byte [] bytes ) <
try <
return new String ( bytes, "UTF-8" ) ;
> catch ( UnsupportedEncodingException e ) <
throw Lombok.sneakyThrow ( e ) ;
>
>

public void run () <
try <
throw new Throwable () ;
> catch ( Throwable t ) <
throw Lombok.sneakyThrow ( t ) ;
>
>
>

Supported configuration keys:

Small print

Because @SneakyThrows is an implementation detail and not part of your method signature, it is an error if you try to declare a checked exception as sneakily thrown when you don’t call any methods that throw this exception. (Doing so is perfectly legal for throws statements to accommodate subclasses). Similarly, @SneakyThrows does not inherit.

For the nay-sayers in the crowd: Out of the box, Eclipse will offer a ‘quick-fix’ for uncaught exceptions that wraps the offending statement in a try/catch block with just e.printStackTrace() in the catch block. This is so spectacularly non-productive compared to just sneakily throwing the exception onwards, that Roel and Reinier feel more than justified in claiming that the checked exception system is far from perfect, and thus an opt-out mechanism is warranted.

If you put @SneakyThrows on a constructor, any call to a sibling or super constructor is excluded from the @SneakyThrows treatment. This is a java restriction we cannot work around: Calls to sibling/super constructors MUST be the first statement in the constructor; they cannot be placed inside try/catch blocks.

@SneakyThrows on an empty method, or a constructor that is empty or only has a call to a sibling / super constructor results in no try/catch block and a warning.

Application of @Sneaky Throws in lombok

I was playing with the Lombok library in Java and found an annotation called @SneakyThrows. As the documentation states:

@SneakyThrows fakes out the compiler. In other words, Lombok doesn’t wrap or replace the thrown checked exception, but makes the compiler think that it is an unchecked exception.

With other words, this is a way to bypass exceptions at compile time. But in my opinion this should not be the correct way of handling exceptions, because the bypassed exception can show weird behaviour at runtime.

So in which scenario should @SneakyThrows be used?

Jacob van Lingen's user avatar

Shivanshu's user avatar

5 Answers 5

To add to the existing answers. I personally dislike checked exceptions. See for more info: https://phauer.com/2015/checked-exceptions-are-evil/

To add insult to injury, the code gets bloated when avoiding the checked exceptions. Consider the usage of @SneakyThrows:

Hence the applicance of @SneakyThrows enables much cleaner code.

SnowmanXL's user avatar

I believe the intention here is to cause the compiler to not require a throws whatever Exception to be added to the method declaration.

For example if the method was

This would cause a compile time exception requiring

The annotation @SneakThrows mitigates this — original method declared as

This will not cause a compile time error. Note IDEs might still highlight this as an error, for example in IntelliJ you will need to utilise the Lombok plugin.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *