Оберните исключения исключениями среды выполнения с аннотацией

StackOverflow https://stackoverflow.com/questions/1052568

Вопрос

Есть ли способ аннотировать метод, чтобы все генерируемые исключения автоматически преобразовывались в исключение времени выполнения?

@MagicAnnotation
// no throws clause!
void foo()
{
  throw new Exception("bar")'
}
Это было полезно?

Решение

Это невозможно сделать, по крайней мере, на данный момент я использую обходной путь, подобный этому (упрощенный):

@SuppressWarnings({"rawtypes", "unchecked"})
public class Unchecked {
    public static interface UncheckedDefinitions{
        InputStream openStream();
        String readLine();
            ...
    }

  private static Class proxyClass = Proxy.getProxyClass(Unchecked.class.getClassLoader(), UncheckedDefinitions.class);

    public static UncheckedDefinitions unchecked(final Object target){
        try{
            return (UncheckedDefinitions) proxyClass.getConstructor(InvocationHandler.class).newInstance(new InvocationHandler(){
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if (target instanceof Class){
                        return MethodUtils.invokeExactStaticMethod((Class) target, method.getName(), args);
                    }

                  return MethodUtils.invokeExactMethod(target, method.getName(), args);
                }
            });
        }
        catch(Exception e){
            throw new RuntimeException(e);
        }
    }
}

И использование выглядит следующим образом:

import static ....Unchecked.*;

...

Writer w = ...;
unchecked(w).write(str, off, len);

Хитрость в том, что интерфейс "никогда не завершен", и каждый раз, когда мне где-то понадобится непроверенный метод, я оборачиваю этот объект в непроверенный и позволяю IDE генерировать сигнатуру метода в интерфейсе.

Тогда реализация является общей (рефлексивной и "медленной", но обычно достаточно быстрой).

Есть несколько постпроцессоров кода и создателей байт-кода, но это было невозможно (даже aop или другой язык, основанный на jvm) для моего текущего проекта, поэтому это было "изобретено".

Другие советы

Проект Ломбока @SneakyThrows вероятно, это то, что вы ищете.На самом деле это не перенос вашего исключения (потому что во многих случаях это может быть проблемой), оно просто не выдает ошибку во время компиляции.

@SneakyThrows
void foo() {
    throw new Exception("bar")'
}

Вы можете сделать это с помощью AspectJ .Вы объявляете точку соединения (в данном случае вызов метода foo) и "смягчаете" исключение.

Редактировать Чтобы немного подробнее остановиться на этом:

Допустим, у вас есть следующий класс Bar:

public class Bar {

    public void foo() throws Exception {
    }
}

... и у вас есть такой тест , как этот:

import junit.framework.TestCase;

public class BarTest extends TestCase {

    public void testTestFoo() {
        new Bar().foo();
    }
}

Тогда, очевидно, тест не будет компилироваться.Это выдаст сообщение об ошибке:

Unhandled exception type Exception  BarTest.java(line 6)

Теперь, чтобы преодолеть это с помощью AspectJ, вы пишете очень простой аспект:

public aspect SoftenExceptionsInTestCode {

    pointcut inTestCode() : execution(void *Test.test*());

    declare soft : Exception : inTestCode();
}

Аспект в основном говорит о том, что любой код из теста (т.е.:метод, который начинается с "test" в классе, который заканчивается на "Test" и возвращает "void"), который выдает исключение, должен быть принят компилятором AspectJ.Если возникнет исключение, оно будет обернуто и выброшено как RuntimeException с помощью компилятора AspectJ.

Действительно, если вы запустите этот тест как часть проекта AspectJ из Eclipse (с установленным AJDT), то тест пройдет успешно, тогда как без aspect он даже не будет скомпилирован.

Я думаю, что это возможно с помощью реинжиниринга байт-кода, настроенного компилятора или, возможно, аспектно-ориентированного программирования1.В отличие от Java, C # имеет только непроверенные исключения2.

Могу я спросить, почему вы хотите подавить проверяемые исключения?

1 согласно Maarten Winkels это возможно.
2 и они подумывают о том, чтобы ввести проверенные, судя по некоторым видеороликам 9-го канала.

Редактировать: Что касается вопроса:Это возможно в том смысле, что вы можете аннотировать свои методы, чтобы пометить их как кандидата на подавление проверяемых исключений.Затем вы используете какой-нибудь трюк времени компиляции или среды выполнения, чтобы применить фактическое подавление / перенос.

Однако, поскольку я не вижу среды вокруг вашего случая, обертывание исключения этими способами может сбить с толку клиентов этого метода - они могут быть не готовы иметь дело с RuntimeException.Например:метод выдает исключение IOException, и ваши клиенты перехватывают его как FileNotFoundException для отображения диалогового окна с ошибкой.Однако, если вы обернете свое исключение в RuntimeException, диалоговое окно с ошибкой никогда не будет показано и, вероятно, это также приведет к уничтожению вызывающего потока.(ИМХО).

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

Вы можете сделать это в любом случае, используя тот факт, что Class.newInstance не делает оберните Exception генерируется конструктором no-arg в InvocationTargetException;скорее, он выбрасывает это молча:

class ExUtil {
  public static void throwSilent(Exception e) { //NOTICE NO THROWS CLAUSE
      tl.set(e);
      SilentThrower.class.newInstance(); //throws silently
  }

  private static ThreadLocal<Exception> tl = new ThreadLocal<Exception>();
  private static class SilentThrower {
      SilentThrower() throws Exception {
          Exception e = tl.get();
          tl.remove();
          throw e;
      }
  }
}

Затем вы можете использовать эту утилиту в любом месте:

ExUtil.throwSilent(new Exception());
//or
try {
  ioMethod();
} catch (IOException e) { ExUtil.throwSilent(e); }

Кстати, это действительно плохая идея :-)

Я использую систему завершения / шаблонов Eclipse, чтобы легко обернуть любой блок кода.

Вот мой шаблон :

try { // Wrapp exceptions

${line_selection}${cursor}

} catch (RuntimeException e) { // Forward runtime exception
throw e;
} catch (Exception e) { // Wrap into runtime exception
throw new RuntimeException(
    "Exception wrapped in #${enclosing_method}", 
    e); 
}

С Java 8 это так же просто, как: soften(() -> methodThatMayThrow())

Читайте больше на http://iirekm.blogspot.com/2014/05/fix-problems-with-java-checked.html

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top