لماذا الاستثناء .fillInstackTrace إرجاع الرميم؟
-
19-09-2019 - |
سؤال
أعتقد أنه استثناء. يجب إرجاع استثناء أو كائنات استثناء مشتق. النظر في الوظيفتين أدناه،
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();
}
}
- ال
exception handler
لا يمكن اللحاقnew Throwable()
في الوظيفة الأولىf()
. - ال
exception handler
يمكن اللحاقe.fillInstackTrace()
في الوظيفة الثانيةg()
. - لكن الوظيفة الثانية
g()
سوف لا تزال بحاجة إلىthrows Throwable
. وبعد هذا غريب حقا، لأننا يمكن أن تصابe.fillInstackTrace()
.
لذا فإن سؤالي هو السبب في استثناء استثناء إرجاع الإرجاع أو الاستمتاع بشكل استثناء بدلا من تطوير بناء الجملة الغريب؟
تعديل:
ل يوضح سؤالي: ما أقصده "بناء الجملة الغريب"
- حيث
Exception.fillInStackTrace()
إرجاعThrowable
مرجع، معالج الاستثناء الذي تلقيException
يجب ألا يكون المرجع قادرا على التقاط الاستثناء. لأن Java لا يسمح بإقامته في قيمته، يجب أن يكون شيء مثلreturn (Exception)e.fillInstackTrace()
. - منذ أن صممت أن معالج الاستثناء الذي تمتلمه
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()
.