Подтвердить что ты не робот

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

У меня есть такой способ:

public void getSomething(){
...
}

Я хочу добавить Exception внутри getSomething(). Компилятор не позволит мне это сделать, потому что мой метод не позволяет туда Exception. Но мне нужно подклассифицировать Exception для моего тестирования (я не могу выбросить Unchecked Exception). Это явно хак, но мне нужно это для моего тестирования. Я попробовал EasyMock, но это тоже не позволяет мне это делать. Любые идеи, как это сделать?

Спасибо, Шон Нгуен

4b9b3361

Ответ 1

Метод 1:

Этот пост Алексея Рагозина описывает, как использовать трюк дженериков, чтобы бросить необъявленное проверенное исключение. Из этого сообщения:

public class AnyThrow {

    public static void throwUnchecked(Throwable e) {
        AnyThrow.<RuntimeException>throwAny(e);
    }

    @SuppressWarnings("unchecked")
    private static <E extends Throwable> void throwAny(Throwable e) throws E {
        throw (E)e;
    }
}

Трюк полагается на throwUnchecked "лежащий" компилятору, что тип E равен RuntimeException с его вызовом throwAny. Поскольку throwAny объявлен как throws E, компилятор считает, что конкретный вызов может просто бросить RuntimeException. Разумеется, трюк стал возможным благодаря throwAny произвольному объявлению E и слепому кастингу на него, позволяющему вызывающему пользователю решить, к чему его аргумент приписывается - ужасный дизайн при правильном кодировании. Во время выполнения E erased и не имеет значения.

Как вы отметили, делать такую ​​вещь - огромный взлом, и вы должны хорошо документировать его использование.


Метод 2:

Вы также можете использовать sun.misc.Unsafe для этой цели. Сначала вы должны реализовать метод, который использует отражение для возврата этого экземпляра класса:

private static Unsafe getUnsafe() {
    try {
        Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafeField.setAccessible(true);
        return (Unsafe)theUnsafeField.get(null);
    }
    catch (NoSuchFieldException e) {
        throw new RuntimeException(e);
    }
    catch (IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

Это необходимо, так как вызов Unsafe.getUnsafe() обычно генерирует SecurityException. Когда у вас есть экземпляр Unsafe, вы можете использовать его ужасающие возможности:

Unsafe unsafe = getUnsafe();
unsafe.throwException(new Exception());

Кредит переходит на этот ответ на сообщение fooobar.com/questions/18702/.... Я думал, что упомянул об этом для полноты, но, вероятно, лучше просто использовать трюк выше, а не разрешать Unsafe в свой код.


Способ 3:

В комментариях связанного ответа об использовании Unsafe @bestsss указывает на более простой трюк, используя устаревший метод Thread.stop(Throwable):

Thread.currentThread().stop(new Exception());

В этом случае вы бы использовали @SuppressWarnings("deprecation") и еще раз документ очень яростно. Опять же, я предпочитаю первый трюк за его (относительную) чистоту.

Ответ 2

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

RuntimeException является основным неконтролируемым исключением, поэтому вы можете выбросить его, не объявив его.

public void getSomething(){
   throw new RuntimeException("I don't have to be declared in the method header!");
}

В качестве побочного примечания вы, вероятно, не хотите бросать необработанное RuntimeException, но подклассируете его в нечто более конкретное для ваших нужд. Любой подкласс RuntimeException также будет снят.

Ответ 3

Я не знаю, что вы пытались сделать. Вы можете просто удалить исключенное исключение внутри своего метода, а затем проверить с помощью JUnit, как в моем примере ниже.

public void getSomething() {
    throw new RuntimeException();
}

Пример теста JUnit 4:

@Test
public void testGetSomething() {
    try {
        getSomething();
        fail("Expecting RuntimeException!");
    } catch (Exception e) {
        Logger.getLogger("test").info("Exception was caught: " + e.getClass());
    }
}