لماذا لا أستطيع استخدام كتلة المحاولة في جميع أنحاء بلدي السوبر() الدعوة ؟

StackOverflow https://stackoverflow.com/questions/5328

سؤال

حتى في جافا, السطر الأول من المنشئ الخاص بك يجب أن يكون دعوة إلى سوبر...سواء كان ذلك ضمنيا الدعوة السوبر () ، أو صراحة استدعاء آخر منشئ.ما أريد أن أعرفه هو لماذا لا يمكنني وضع كتلة المحاولة حول ذلك ؟

بلدي حالة معينة هو أن لدي الدرجة وهمية لاختبار.لا يوجد منشئ افتراضي, ولكن أريد واحدة لجعل الاختبارات أسهل للقراءة.أنا أيضا أريد أن التفاف استثناءات من المنشئ إلى RuntimeException.

ما أريد القيام به هو فعالية هذا:

public class MyClassMock extends MyClass {
    public MyClassMock() {
        try {
            super(0);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // Mocked methods
}

ولكن جافا يشكو من أن السوبر ليست أول بيان.

بلدي الحل:

public class MyClassMock extends MyClass {
    public static MyClassMock construct() {
        try {
            return new MyClassMock();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public MyClassMock() throws Exception {
        super(0);
    }

    // Mocked methods
}

هل هذا أفضل ما الحل?لماذا لا جافا اسمحوا لي أن تفعل السابق ؟


أفضل تخمين "لماذا" هو أن جافا لا أريد أن اسمحوا لي أن يكون شيدت الكائن في يحتمل أن تكون حالة غير متناسقة...ومع ذلك ، في يسخرون أنا لا يهمني ذلك.يبدو أنني يجب أن تكون قادرة على القيام أعلاه...أو على الأقل أعرف أن ما ورد أعلاه هو آمن حالتي...أو يبدو كما لو أنه ينبغي أن يكون على أي حال.

أنا الغالبة أي أساليب استخدام من اختبار الفصل لذلك ليس هناك خطر من أن أنا باستخدام المتغيرات غير مهيأ.

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

المحلول

للأسف المجمعين لا يمكن العمل على المبادئ النظرية ، على الرغم من أنك قد تعرف أنها آمنة في الحالة الخاصة بك ، إذا سمحوا ذلك ، فإنه يجب أن تكون آمنة في جميع الحالات.

وبعبارة أخرى ، فإن المترجم لا يتوقف فقط لك ، إنه وقف الجميع ، بما فيها تلك التي لا تعرف أنه غير آمن ولا يحتاج إلى معالجة خاصة.ربما يكون هناك أسباب أخرى لهذه وكذلك جميع اللغات عادة الطرق غير آمنة الأشياء إذا أحد يعرف كيفية التعامل معهم.

في C# .صافي وهناك أحكام مماثلة, و الطريقة الوحيدة لإعلان منشئ أن يدعو قاعدة منشئ هو هذا:

public ClassName(...) : base(...)

في القيام بذلك, قاعدة منشئ سوف يطلق قبل هيئة منشئ ، و لا يمكنك تغيير هذا النظام.

نصائح أخرى

يتم ذلك لمنع أي شخص من خلق جديد SecurityManager كائن من التعليمات البرمجية غير الموثوق بها.

public class Evil : SecurityManager {
  Evil()
  {
      try {
         super();
      } catch { Throwable t }
      {
      }
   }
}

أعرف أن هذا هو السؤال القديم, ولكن أنا أحب ذلك, وعلى هذا النحو, أنا قررت أن تعطيه جوابا من بلدي.ربما فهمي لماذا هذا لا يمكن القيام به سوف تسهم في مناقشة المستقبل القراء من مسألة مثيرة للاهتمام.

اسمحوا لي أن أبدأ مع مثال على الفشل في بناء الكائن.

دعونا نحدد فئة A, مثل:

class A {
   private String a = "A";

   public A() throws Exception {
        throw new Exception();
   }
}

الآن, دعونا نفترض أننا نود أن إنشاء كائن من نوع A في try...catch كتلة.

A a = null;
try{
  a = new A();
}catch(Exception e) {
  //...
}
System.out.println(a);

ومن الواضح أن إخراج هذه المدونة سوف تكون: null.

لماذا جافا لا يعود جزئيا شيدت إصدار A?بعد كل نقطة منشئ فشل الكائن name المجال وقد تم بالفعل تهيئة صحيح ؟

حسنا, جافا لا يعود جزئيا شيدت إصدار A لأن الهدف لم يكن بنيت بنجاح.الكائن في حالة غير متناسقة ، ومن ثم التخلص منها عن طريق جافا.المتغير الخاص بك ليست حتى تهيئة ، فإنه يتم الاحتفاظ بها باطل.

الآن كما تعلمون أن بناء بالكامل وجوه جديدة, كل سوبر دروس يجب تهيئة الأولى.إذا كان واحد من السوبر دروس فشل تنفيذ ما سيكون نهائي الدولة الكائن ؟ فإنه من المستحيل تحديد ذلك.

انظر إلى هذا أكثر تفصيلا سبيل المثال

class A {
   private final int a;
   public A() throws Exception { 
      a = 10;
   }
}

class B extends A {
   private final int b;
   public B() throws Exception {
       methodThatThrowsException(); 
       b = 20;
   }
}

class C extends B {
   public C() throws Exception { super(); }
}

عندما منشئ C هو الاحتجاج في حالة حدوث استثناء أثناء تهيئة B, ماذا ستكون قيمة النهائي int متغير b?

مثل الكائن C لا يمكن إنشاء ، فمن وهمية ، فمن القمامة ، فإنه ليس من تهيئة بشكل كامل.

بالنسبة لي هذا ما يفسر لماذا رمز الخاص بك هو غير قانوني.

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

لذلك فإنه ليس حتى خطر uninited المتغيرات أو شيء من هذا القبيل على الإطلاق.عندما كنت تحاول أن تفعل شيئا في فرعية' منشئ قبل الفئة الأساسية' منشئ, أنت أساسا يسأل المترجم إلى توسيع قاعدة مثيل كائن لا وجود له حتى الآن.

تحرير:في الحالة الخاصة بك ، MyClass يصبح قاعدة كائن ، MyClassMock هو فئة فرعية.

أنا لا أعرف كيف جافا يتم تنفيذها داخليا ، ولكن إذا كان منشئ الفائقة يطرح استثناء ، ثم ليس هناك مثيل من فئة يمكنك تمديد.سيكون من المستحيل الاتصال toString() أو equals() الأساليب ، على سبيل المثال ، لأنها ورثت في معظم الحالات.

جافا قد تسمح حاول/catch حول السوبر() استدعاء منشئ إذا 1.يمكنك تجاوز كل الأساليب من superclasses ، و 2.لا يمكنك استخدام السوبر.XXX() فقرة ، ولكن كل هذا يبدو معقدا جدا بالنسبة لي.

أعرف أن هذا السؤال له عدة إجابات ، ولكن أود أن أعطي الحكاية الصغيرة على لماذا هذا لن يكون مسموحا به ، على وجه التحديد إلى إجابة لماذا جافا لا تسمح لك أن تفعل هذا.حتى هنا تذهب...

الآن نضع في اعتبارنا أن super() وقد تسمى قبل أي شيء آخر في فئة فرعية منشئ ، لذا ، إذا كنت لم تستخدم try و catch كتل حول super() دعوة الكتل يجب أن تبدو مثل هذا:

try {
   super();
   ...
} catch (Exception e) {
   super(); //This line will throw the same error...
   ...
}

إذا كان السوبر()fails in theمحاولةblock, it HAS to be executed first in theقبضblock, so thatسوبرruns before anything in your subclasss منشئ.هذا يترك لك مع نفس المشكلة لديك في البداية:إذا تم طرح استثناء ، فإنه لم يتم القبض.(في هذه الحالة فإنه يحصل فقط ألقيت مرة أخرى في كتلة catch.)

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

الآن, والسبب أن جافا لا تمكنك من رمي استثناء بدلا من ذلك الدعوة super() لأن الاستثناء يمكن أن تكون اشتعلت في مكان آخر ، و البرنامج سيستمر دون استدعاء super() على فرعية كائن ، وربما لأن الاستثناء قد يستغرق وجوه الخاص بك كمعلمة ومحاولة تغيير قيمة ورثت سبيل المثال المتغيرات التي قد لا بعد تم تهيئة.

طريقة واحدة للحصول على من حوله هو طريق استدعاء خاص دالة ثابتة.حاول اللحاق ثم يمكن وضعها في وظائف الجسم.

public class Test  {
  public Test()  {
     this(Test.getObjectThatMightThrowException());
  }
  public Test(Object o)  {
     //...
  }
  private static final Object getObjectThatMightThrowException()  {
     try  {
        return  new ObjectThatMightThrowAnException();
     }  catch(RuntimeException rtx)  {
        throw  new RuntimeException("It threw an exception!!!", rtx);
     }
  }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top