Warum kann ich keinen try-Block für meinen Super () - Aufruf verwenden?

  • In Java muss also die erste Zeile Ihres Konstruktors ein Aufruf von super sein. Sei es implizit super () oder explizit einen anderen Konstruktor. Was ich wissen möchte, ist, warum kann ich das nicht mit einem try-Block umgehen?

    Mein besonderer Fall ist, dass ich eine Mock-Klasse für einen Test habe. Es gibt keinen Standardkonstruktor, aber ich möchte, dass die Tests einfacher zu lesen sind. Ich möchte auch die vom Konstruktor geworfenen Ausnahmen in eine RuntimeException einschließen.

    Was ich also machen möchte, ist im Grunde Folgendes:

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

    Java beschwert sich jedoch, dass Super nicht die erste Anweisung ist.

    Meine Problemumgehung:

     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
    }
     

    Ist dies die beste Problemumgehung? Warum lässt Java mich nicht auf die frühere Version anwenden?


    Meine beste Vermutung hinsichtlich des "Warum" ist, dass Java nicht will Ich möchte, dass sich ein Objekt in einem möglicherweise inkonsistenten Zustand befindet. Wenn ich jedoch einen Spott mache, ist mir das egal. Es scheint, dass ich in der Lage sein sollte, das Obige zu tun ... oder zumindest weiß ich, dass das Obige für meinen Fall sicher ist ... oder so scheint, als ob es sowieso sein sollte.

    Ich überschreibe alle Methoden, die ich aus der getesteten Klasse verwende. Daher besteht nicht das Risiko, dass ich nicht initialisierte Variablen verwende.

    06 November 2014
    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaKevin Gao
7 answers
  • Leider können Compiler nicht an theoretischen Prinzipien arbeiten, und obwohl Sie vielleicht wissen, dass es in Ihrem Fall sicher ist, müssten sie, wenn sie es zulassen, für alle Fälle sicher sein.

    Mit anderen Worten, der Compiler stoppt nicht nur Sie, sondern jeden, auch alle, die nicht wissen, dass er unsicher ist und eine spezielle Behandlung erfordert. Es gibt wahrscheinlich auch andere Gründe dafür, da alle Sprachen normalerweise unsichere -Dinge tun, wenn man weiß, wie sie damit umgehen sollen.

    In In C # .NET gibt es ähnliche Bestimmungen, und die einzige Möglichkeit, einen Konstruktor zu deklarieren, der einen Basiskonstruktor aufruft, lautet:

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

    Dabei wird der Basiskonstruktor vor dem Rumpf des Konstruktors aufgerufen, und Sie können diese Reihenfolge nicht ändern.

    08 August 2008
    Lasse Vågsæther Karlsen
  • Dies soll verhindern, dass jemand ein neues SecurityManager Objekt aus nicht vertrauenswürdigem Code erstellt.

     public class Evil : SecurityManager {
      Evil()
      {
          try {
             super();
          } catch { Throwable t }
          {
          }
       }
    }
     
    01 November 2011
    Jason Planke2p
  • Ich weiß, dass dies eine alte Frage ist, aber ich mochte sie, und deshalb entschied ich mich, sie selbst zu beantworten. Vielleicht trägt mein Verständnis dafür, warum dies nicht möglich ist, zur Diskussion und zu zukünftigen Lesern Ihrer interessanten Frage bei.

    Lassen Sie mich mit einem Beispiel für eine fehlerhafte Objektkonstruktion beginnen.

    Definieren Sie eine Klasse A wie folgt:

     class A {
       private String a = "A";
    
       public A() throws Exception {
            throw new Exception();
       }
    }
     

    Nun, Nehmen wir an, wir möchten ein Objekt vom Typ A in einem try...catch -Block erstellen.

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

    Offensichtlich Die Ausgabe dieses Codes lautet: null.

    Warum gibt Java keine teilweise konstruierte Version von A zurück? Zu dem Zeitpunkt, an dem der Konstruktor fehlschlägt, wurde das Feld name des Objekts bereits initialisiert, oder?

    Java kann keine teilweise konstruierte Version von [&&5 && zurückgeben. ] weil das Objekt nicht erfolgreich erstellt wurde. Das Objekt befindet sich in einem inkonsistenten Zustand und wird daher von Java verworfen. Ihre Variable A ist nicht einmal initialisiert, sondern wird als Nullwert beibehalten.

    Um nun ein neues Objekt vollständig zu erstellen, müssen zunächst alle seine Superklassen initialisiert werden. Wenn eine der Superklassen nicht ausgeführt werden könnte, wie lautet der endgültige Status des Objekts? Das lässt sich nicht feststellen.

    Sehen Sie sich dieses ausführlichere Beispiel an

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

    Wenn der Konstruktor von C aufgerufen wird und eine Ausnahme beim Initialisieren von B auftritt, wie lautet der Wert der letzten int -Variable b?

    Daher kann das Objekt C nicht erstellt werden, es ist gefälscht, es ist Abfall, es ist nicht vollständig initialisiert.

    Dies erklärt für mich, warum Ihr Code ist illegal.

    17 April 2017
    KröwEdwin Dalorzo
  • Ich weiß nicht, wie Java intern implementiert wird, aber wenn der Konstruktor der Superklasse eine Ausnahme auslöst, gibt es keine Instanz der Klasse, die Sie erweitern. Es ist beispielsweise nicht möglich, die Methoden toString() oder equals() aufzurufen, da sie in den meisten Fällen vererbt werden. Java kann einen Try / Catch um den Super-Server zulassen () Aufruf im Konstruktor, wenn 1. Sie ALLE Methoden der Superklassen überschreiben und 2. Sie nicht die Klausel super.XXX () verwenden, aber das klingt für mich zu kompliziert.

    08 November 2011
    mskfisherDavid Saltares
  • Ich kann nicht davon ausgehen, ein tiefes Verständnis für Java-Interna zu haben, aber ich verstehe, dass ein Compiler, wenn er eine abgeleitete Klasse instanziieren muss, zuerst die Basis (und ihre Basis davor) erstellen muss (...)) und dann auf die Erweiterungen der Unterklasse klatschen.

    Es besteht also nicht einmal die Gefahr von nicht geladenen Variablen oder dergleichen. Wenn Sie versuchen, etwas im Konstruktor der Unterklasse vor des -Konstruktors der Basisklasse zu tun, bitten Sie den Compiler im Wesentlichen, eine Basisobjektinstanz zu erweitern, die noch nicht vorhanden ist .

    Bearbeiten: In Ihrem Fall wird MyClass zum Basisobjekt und MyClassMock ist eine Unterklasse.

    08 August 2008
    Ishmaeel
  • Ich weiß, dass diese Frage zahlreiche Antworten hat, aber ich möchte gerne etwas dazu sagen, warum dies nicht erlaubt ist, insbesondere um zu beantworten, warum Java dies nicht zulässt. Also, los geht's ...

    Denken Sie daran, dass super() vor allen anderen Elementen im Konstruktor einer Unterklasse aufgerufen werden muss. Wenn Sie also try und catch Blöcke um Ihren super() -Aufruf müssen die Blöcke folgendermaßen aussehen:

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

    Wenn super () fails in the versucht, block, it HAS to be executed first in the catch block, so that super runs before anything in your subclass s-Konstruktor zu verwenden. Damit haben Sie das gleiche Problem, das Sie zu Beginn hatten: Wenn eine Ausnahme ausgelöst wird, wird sie nicht abgefangen. (In diesem Fall wird es einfach wieder in den catch-Block geworfen.)

    Nun ist der obige Code auch in Java in keiner Weise erlaubt. Dieser Code kann die Hälfte des ersten Super-Aufrufs ausführen und dann erneut aufrufen, was bei einigen Superklassen zu Problemen führen kann.

    Nun, der Grund, den Java nicht zulässt Sie werfen eine Ausnahme instea

    17 April 2017
    KröwEdwin Dalorzo
  • Um dies zu umgehen, rufen Sie eine private statische Funktion auf. Der Try-Catch kann dann in den Funktionskörper eingefügt werden.

     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);
         }
      }
    }
     
    06 November 2014
    aliteralmind