سؤال

أعتقد أنه استثناء. يجب إرجاع استثناء أو كائنات استثناء مشتق. النظر في الوظيفتين أدناه،

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().

لذا فإن سؤالي هو السبب في استثناء استثناء إرجاع الإرجاع أو الاستمتاع بشكل استثناء بدلا من تطوير بناء الجملة الغريب؟

تعديل:
ل يوضح سؤالي: ما أقصده "بناء الجملة الغريب"

  1. حيث Exception.fillInStackTrace() إرجاع Throwable مرجع، معالج الاستثناء الذي تلقي Exception يجب ألا يكون المرجع قادرا على التقاط الاستثناء. لأن Java لا يسمح بإقامته في قيمته، يجب أن يكون شيء مثل return (Exception)e.fillInstackTrace().
  2. منذ أن صممت أن معالج الاستثناء الذي تمتلمه Exception المرجع يمكن التعامل مع Throwable استثناء، ليست هناك حاجة لتمييز الطريقة g() رمي Throwable Exception.but Java Compiler من شأنه أن يفرضنا القيام بذلك.

شكرا.

هل كانت مفيدة؟

المحلول

من الأسهل في الواقع الإجابة على أسئلتك بدءا من السؤال 2.

سألت: 2. نظرا لأنه تم تصميمه أن مرجع استثناء بمعالج الاستثناء يمكن أن يتعامل مع الاستثناء القابل للترحيل، لا توجد حاجة لوضع علامة على طريقة G () طرقا استثناءا قابلة للتخلص.

الإجابة: في الواقع، لا يمكن للقبض (الاستثناء ه) اللحاق بالركض. جرب هذا:

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

سترى أن جملة الصيد لا يمسك رمي في هذه الحالة.

السبب في أن جملة الصيد يعمل في طريقة G () الخاصة بك هو أنه عند استدعاء throw e.fillInStackTrace(), ، تتمتع الدعوة إلى fillinstackTrace فعلا باستثناء استثناء (ذلك لأن E هو استثناء نفسه). نظرا لأن الاستثناء هو فئة فرعية من التخلص منها، فإن ذلك لا يتعارض مع إعلان FillinstackTrace.

الآن على السؤال الأول

طلب منك: 1. منذ استثناء. fillinstackTrace ().

الإجابة: هذا ليس بالضبط سهلة الاستخدام الضمني. فكر في هذا باعتباره تباين من التحميل الزائد.

دعنا نقول أن لديك

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

إذا اتصلت process(someObject), ، سيتم تحديده في وقت التشغيل ما إذا كانت طريقة العملية الأولى أو الثانية التي يتم استدعاؤها. وبالمثل، ما إذا كان يمكن تحديد جملة الصيد (الاستثناء E) أو لا يتم تحديد رميك في وقت التشغيل، بناء على ما إذا كنت ترمي استثناءا أو قابلة للإخراج.

نصائح أخرى

أنا أحير إلى حد ما عن سؤالك. من الواضح أن هناك شيئا ما حول استثناءات Java / معالجة الاستثناءات التي لا تفهمها. لذلك دعونا نبدأ في البداية.

في جاوة، جميع الاستثناءات (بمعنى أن هذا المصطلح يستخدم في مواصفات لغة Java) هي مثيلات من بعض الفئة فئة فرعية من java.lang.Throwable. وبعد هناك اثنين (واثنين فقط) الفئات الفرعية المباشرة من التخلص منها؛ بمعنى آخر java.lang.Exception و java.lang.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 الطريقة غير جذابة المثيل القابل للرمي: لا تتطابق مع نوع الاستثناء في جملة الصيد! على النقيض من ذلك، في g الطريقة التي تطابق مثيل الاستثناء من نوع الاستثناء في جملة الصيد وبالتالي يتم اكتشافها.

أنا لا أفهم ما تقوله حول الحاجة إلى رمي قابلة للرمي في g. وبعد أولا، حقيقة أن الطريقة تعلن أنه يلقي بالرم لا يعني أنه يحتاج في الواقع لرميها. كل ما يقوله هو أنه ربما رمي شيء واحد قابلة للتخلص ... ربما في بعض النسخة المستقبلية من g طريقة. ثانيا، إذا كنت تضيف throw e; إلى كتلة الصيد الخارجية، سيكون أن رمي شيء قابل للترخيص.

أخيرا، من الفكرة السيئة عموما أن تخلق مثيلات من التخلص منها واستثناء وخطأ و 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, 
}

تعديل - ردا على تعليقات OP:

ولكن ما رمي مع رمي e.fillInstackTrace ()؛ يجب أن يكون ماء القابلة للإزالة، وليس استثناء!

يقول Javadoc على وجه التحديد أن الكائن الذي تم إرجاعه هو كائن الاستثناء الذي تتصل بهذه الطريقة. الغرض من fillInStacktrace() الطريقة هي ملء تتبع المكدس لكائن موجود. إذا كنت تريد استثناء مختلف، يجب عليك استخدام new لخلق واحد.

في الواقع، أعني أن معالج الاستثناء الخارجي لا ينبغي أن يقبض على الرمي عن طريق رمي e.fillInstackTrace ().

لقد أوضحت لماذا يفعل ذلك - لأن الروم هو في الواقع الاستثناء الأصلي. هل هناك شيء حيال تفسيري أنك لا تفهم أم أنك تقول ببساطة أنك لا تحب الطريقة التي يتم تعريف جافا بها؟

تحرير 2.

وإذا كان معالج الاستثناء الخارجي يمكنه التعامل مع الاستثناء القابل للتخلص، فلماذا نحدد أن الطريقة G سوف ترمي استثناءا

أنت تسيء فهم ما كنت أقول ... الذي كان ذلك إذا قمت بإلقاء استثناء، ثم throws Throwable لن تكون زائدة عن الحاجة. أوتوه، أعتقد أخيرا أنني أفهم شكواك.

أعتقد أن جواد شكواك هو أنك تحصل على خطأ في تجميع مع هذا:

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 التي ستعمل في جميع الحالات. على سبيل المثال، إذا قمت بنقل إعلان الطريقة إلى فئة الاستثناء، فسوف تكرر نفس المشكلة نفسها مع فرعية استثناء. ولكن إذا حاولت التعبير عن التوقيع باستخدام معلمة نوع عام، فسوف ينطوي على جعل جميع الفئات الفرعية من الأنواع العامة الصريحة.

لحسن الحظ، العلاج بسيط حقا؛ يلقي نتيجة 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 وما بعدها، أنواع عودة covariant يسمح به وما سبق هو قانونية.

لكن تخميني هو أنه إذا كان التجاوز أعلاه موجود، فستسئل عن السبب 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