Почему Exception.fillInStackTrace возвращает Throwable?

Я думаю, что Exception.fillInStackTrace должен возвратить Исключение или полученные Объекты исключения. Рассмотрение двух функций ниже,

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 или Полученный из исключения вместо того, чтобы разработать такой странный синтаксис?

Править:
Разъяснить мой вопрос: Что я подразумеваю "под странным синтаксисом",

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

спасибо.

5
задан Jichao 8 January 2010 в 08:53
поделиться

6 ответов

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

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

Ответ: На самом деле, поймать (исключение E) не может поймать бросок. Попробуйте это:

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

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

Причина, по которой работает предложение CATT в вашем методе G (), заключается в том, что при вызове бросают E.FILLINSTACTTRACE () , вызов на FillInStacktrace на самом деле возвращает исключение (это потому, что E - это сам исключение ). Поскольку исключение представляет собой подкласс бросаемого, который не противоречит декларации FillInStacktrace.

Теперь к первому вопросу

вы спросили: 1. Поскольку Excellion.fillinStacktrace () возвращается мошенническую ссылку, обработчик исключения, которые получают ссылку на исключение, которые получают ссылку на исключение, не должны иметь возможность поймать исключение. Потому что Java не позволяет им поднимать, это должно быть что-то вроде возврата (исключение) E.FillinStacktrace () Отказ

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

Давайте скажем, у вас есть

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

, если вы звоните процесс (какой-тоobject) , он будет определен во время выполнения, то называется первый или второй метод процесса. Точно так же, может ли пункт CALL (Exception E) может поймать ваш бросок, будет определен во время выполнения, основываясь на том, вы бросаете ли вы исключение или бросать.

6
ответ дан 18 December 2019 в 05:49
поделиться

Я несколько озадачен вашим вопросом. В 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 in g . Во-первых, тот факт, что метод объявляет, что он выбрасывает Throwable , не означает , что ему действительно нужно его выбросить. Все, что он говорит, это то, что он может выбросить что-то, что можно назначить Throwable ... возможно, в какой-нибудь будущей версии метода g . Во-вторых, если бы вы добавили throw e; во внешний блок catch, он выбросил бы что-то, что можно назначить для 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, 
}

EDIT - в ответ на комментарий OP:

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

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

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

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

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

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

. Вы неправильно поняли, о чем я говорил ... а именно, если вы ДЕЙСТВИТЕЛЬНО выбросили исключение, то выбрасывает 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 просто не стоило «напрягать кишки», пытаясь решить эту проблему. Тем более, что это действительно мелкие неудобства ... самое большее.

12
ответ дан 18 December 2019 в 05:49
поделиться

fillInStackTrace возвращает ссылку на тот же объект. Это метод chaining, позволяющий перебрасывать 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
3
ответ дан 18 December 2019 в 05:49
поделиться

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

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

2
ответ дан 18 December 2019 в 05:49
поделиться

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

E.FILLINSTACTTRACE == E всегда правда

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

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

или использовать CAST

    throw (Exception) e.fillInStackTrace();

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

2
ответ дан 18 December 2019 в 05:49
поделиться

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

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();
}

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

4
ответ дан 18 December 2019 в 05:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: