Вопрос

Я думаю, что Exception.fillInStackTrace должен возвращать Exception или производные объекты Exception.Учитывая две функции ниже,

public static void f() throws Throwable {
    try {
        throw new Throwable();
    } catch (Exception e) {
        System.out.println("catch exception e");
        e.printStackTrace();
    } 
}
public static void g() throws Throwable {
    try {
        try {
            throw new Exception("exception");
        } catch (Exception e) {
            System.out.println("inner exception handler");
            throw e.fillInStackTrace();
        }
    } catch (Exception e) {
        System.out.println("outer exception handler");
        e.printStackTrace();
    }
}
  1. В exception handler не смог поймать new Throwable() в первой функции f().
  2. В exception handler мог бы поймать e.fillInstackTrace() во второй функции g().
  3. Но вторая функция g() все равно придется throws Throwable.Это действительно странно, поскольку мы могли поймать e.fillInstackTrace().

Итак, мой вопрос: почему Exception.fillInStackTrace не возвращает Exception или производное от Exception вместо разработки такого странного синтаксиса?

РЕДАКТИРОВАТЬ:
К объяснить мой вопрос:Под «странным синтаксисом» я подразумеваю следующее:

  1. С Exception.fillInStackTrace() возвращаться Throwable ссылка, обработчик исключений, который получает Exception ссылка не должна перехватывать исключение. Поскольку Java не допускает неявного понижения уровня, это должно быть что-то вроде return (Exception)e.fillInstackTrace().
  2. Поскольку спроектировано так, что обработчик исключений, получающий Exception ссылка может обрабатывать Throwable исключение, нет необходимости отмечать метод g() бросает Throwable исключение. Но компилятор Java заставит нас сделать это.

Спасибо.

Это было полезно?

Решение

На самом деле проще ответить на ваши вопросы, начиная с вопроса 2.

Вы спрашивали:2.Поскольку разработано так, что обработчик исключений, получающий ссылку на исключение, может обрабатывать исключение Throwable, нет необходимости отмечать метод g(), выбрасывающий исключение Throwable. Но компилятор Java заставит нас сделать это.

Отвечать:На самом деле, catch(Exception e) не может перехватить Throwable.Попробуй это:

try {
       Throwable t = new Throwable();
       throw t.fillInStackTrace();
} catch (Exception e) {
    System.out.println("outer exception handler");
    e.printStackTrace();
} 

Вы увидите, что в этом случае предложение catch не перехватывает бросок.

Причина, по которой предложение catch работает в вашем методе g(), заключается в том, что при вызове throw e.fillInStackTrace(), вызов fillInStackTrace фактически возвращает исключение (это потому, что e само является исключением).Поскольку Exception является подклассом Throwable, это не противоречит объявлению fillInStackTrace.

Теперь переходим к первому вопросу

Вы спрашивали:1.Поскольку Exception.fillInStackTrace() возвращает ссылку Throwable, обработчик исключений, который получает ссылку на исключение, не должен иметь возможности перехватывать исключение. Поскольку Java не допускает неявного понижения уровня, это должно быть что-то вроде return (Exception)e.fillInstackTrace().

Отвечать:Это не совсем скрытое унижение.Думайте об этом как о разновидности перегрузки.

Допустим, у вас есть

void process(Throwable t){
   ...
}
void process(Exception e){
  ...
} 

Если вы позвоните process(someObject), во время выполнения будет определено, будет ли вызван первый или второй метод процесса.Аналогичным образом, может ли предложение catch(Exception e) перехватить ваш бросок, будет определено во время выполнения в зависимости от того, генерируете ли вы Exception или Throwable.

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

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

В Java все исключения (в том смысле, в котором этот термин используется в спецификации языка Java) являются экземплярами некоторого класса, который является подклассом java.lang.Throwable.Есть два (и только два) прямых подкласса Throwable;то есть java.lang.Exception и java.lang.Error.Экземпляры всех этих классов...включая экземпляры Throwable и Error...называются исключениями в JLS.

Обработчик исключений перехватывает исключения (в смысле JLS), которые по назначению совместимы с типом исключения, используемым в catch декларация.Так, например:

try {
    ....
} catch (Exception ex) {
    ...
}

перехватит любое исключение, выброшенное в try блок, который является экземпляром java.lang.Exception или прямого или косвенного подтипа java.lang.Exception.Но он не поймает экземпляр java.lang.Throwable, потому что это (очевидно) не одно из вышеперечисленных.

С другой стороны:

try {
    ....
} catch (Throwable ex) {
    ...
}

воля поймать экземпляр java.lang.Throwable.

Рассматривая ваш пример в свете этого, становится очевидным, почему f метод не перехватывает экземпляр Throwable:оно не соответствует типу исключения в предложении catch!Напротив, в g метод, экземпляр Exception соответствует типу исключения в предложении catch и поэтому перехватывается.

Я не понимаю, что вы говорите о необходимости добавить Throwable. g.Во-первых, тот факт, что метод заявляет, что он выбрасывает Throwable не означает, что его действительно нужно бросить.Все, что там говорится, это то, что это мощь бросить что-то, назначаемое Throwable...возможно, в какой-нибудь будущей версии g метод.Во-вторых, если бы вы добавили throw e; к внешнему блоку захвата, он бы выбрасывать что-то, что можно назначить Throwable.

Наконец, как правило, создавать экземпляры Throwable, Exception, Error и RuntimeException — плохая идея.И нужно быть очень осторожным, когда и как их ловить.Например:

try {
     // throws an IOException if file is missing
    InputStream is = new FileInputStream("someFile.txt");
    // do other stuff
} catch (Exception ex) {
    System.err.println("File not found");
    // WRONG!!!  We might have caught some completely unrelated exception;
    // e.g. a NullPointerException, StackOverflowError, 
}

РЕДАКТИРОВАТЬ - в ответ на комментарии ОП:

Но что я бросаю с помощью throw e.fillInStackTrace();должен быть Intance of Throwable, а не Exception!

В Javadoc конкретно говорится, что возвращаемый объект является объектом исключения, для которого вы вызываете метод.Цель fillInStacktrace() Метод заключается в заполнении трассировки стека для существующего объекта.Если вам нужно другое исключение, вы должны использовать new чтобы создать его.

На самом деле я имею в виду, что внешний обработчик исключений не должен перехватывать Throwable, генерируемый throw e.fillInStackTrace().

Я объяснил, почему это происходит: потому что Throwable на самом деле является оригинальным исключением.Есть ли что-то в моем объяснении, чего вы не понимаете, или вы просто говорите, что вам не нравится способ определения Java?

РЕДАКТИРОВАТЬ 2

И если внешний обработчик исключений может обрабатывать исключение Throwable, почему мы должны указывать, что метод g будет вызывать исключение Throwable?

Вы неправильно поняли то, что я сказал...заключалось в том, что если вы ДЕЙСТВИТЕЛЬНО выдали исключение, то throws Throwable не будет лишним.OTOH, мне наконец кажется, что я понимаю твою жалобу.

Я думаю, что суть вашей жалобы в том, что вы получите ошибку компиляции:

public void function() throws Exception {
    try {
        throw new Exception();
    } catch (Exception ex) {
        throw ex.fillInStackTrace();
        // according to the static type checker, the above throws a Throwable
        // which has to be caught, or declared as thrown.  But we "know" that the 
        // exception cannot be anything other than an Exception.
    }
}

Я вижу, что это несколько неожиданно.Но боюсь, это неизбежно.НЕТ способа (если не считать серьезных изменений в системе типов Java) объявить подпись fillInStacktrace это будет работать во всех случаях.Например, если вы переместите объявление метода в класс Exception, вы просто повторите ту же проблему с подтипами Exception.Но если вы попытаетесь выразить подпись с помощью параметра универсального типа, это повлечет за собой создание всех подклассов Throwable явных универсальных типов.

К счастью, лекарство действительно простое;бросить результат fillInStacktrace() следующее:

public void function() throws Exception {
    try {
        throw new Exception();
    } catch (Exception ex) {
        throw (Exception) (ex.fillInStackTrace());
    }
}

И последнее: очень необычно, чтобы приложение явно вызывало fillInStacktrace().В свете этого разработчикам Java просто не стоило «напрягаться», пытаясь решить эту проблему.Тем более, что это действительно лишь незначительное неудобство...в большинстве.

Я думаю, вам стоит спросить Фрэнка Йеллина ( @author из java.lang.Exception).Как говорили другие, fillInStackTrace() объявлено в Throwable и документально подтверждено возвращение this, поэтому его возвращаемый тип должен быть Throwable.Это просто унаследовано Exception.Фрэнк мог бы обойти это в Exception, так:

public class Exception extends Throwable{
    /**Narrows the type of the overridden method*/
    @Override
    public synchronized Exception fillInStackTrace() {
        super.fillInStackTrace();
        return this;
    }
}

...но он этого не сделал.До версии Java 1.5 причина заключалась в том, что это приводило к ошибке компиляции.В версии 1.5 и более поздних версиях ковариантные типы возвращаемых значений. разрешается и вышеописанное является законным.

Но я предполагаю, что если бы указанное выше переопределение существовало, вы бы спросили, почему RuntimeException не имеет аналогичного переопределения, почему ArrayStoreException не отменяет это переопределение и так далее.Так что Фрэнк и его друзья, вероятно, просто не хотели писать эти сотни одинаковых переопределений.

Стоит отметить, что если вы имеете дело со своими собственными классами исключений, вы можете легко переопределить fillinStackTrace() метод, как я сделал выше, и получите более узкий тип, который вам нужен.

Используя дженерики, можно представить себе расширение объявления Throwable к:

public class Throwable<T extends Throwable<T>>{
    public synchronized native T fillInStackTrace();
}

...но это не совсем удовлетворительно по причинам, выходящим за рамки этого ответа.

fillInStackTrace возвращает ссылку на тот же объект.Это цепочка методов, позволяющая повторно выбрасывать Exception и сброс трассировки стека исключения.

public static void m() {
    throw new RuntimeException();

}

public static void main(String[] args) throws Throwable {
    try {
        m();
    } catch (Exception e) {
        e.printStackTrace();
        throw e.fillInStackTrace();
    }
}

Тип возвращаемого значения метода может быть только базовым типом, который Throwable.Его можно обобщить так, чтобы метод возвращал параметризованный тип.Но на данный момент это не так.

RuntimeException e = new RuntimeException();
Throwable e1 = e.fillInStackTrace();
System.out.println(e1.getClass().getName()); //prints java.lang.RuntimeException
System.out.println(e == e1); //prints true

fillInStackTrace() определяется Throwable, нет Exception.

Не все подклассы Throwable являются исключениями (Error, например).Насколько Throwable это касается, единственное, что он может гарантировать в отношении возвращаемого значения fillInStackTrace() в том, что это экземпляр Throwable (поскольку он просто возвращает тот же объект, что и Чандра Патни отмеченный).

На самом деле fillInStackTrace возвращает тот же объект, для которого он был вызван.

e.fillInStackTrace == e всегда правда

Это просто ярлык, вы также можете просто написать

    } catch (Exception e) {
        System.out.println("inner exception handler");
        e.fillInStackTrace();
        throw e;
    }

или используйте приведение

    throw (Exception) e.fillInStackTrace();

Кстати, то же самое происходит и с initCause().

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