Domanda

Quindi, in Java, la prima riga del tuo costruttore DEVE essere una chiamata a super...sia che si chiami implicitamente super() o che si chiami esplicitamente un altro costruttore.Quello che voglio sapere è: perché non posso inserire un blocco try attorno a questo?

Il mio caso specifico è che ho una lezione simulata per un test.Non esiste un costruttore predefinito, ma ne voglio uno per rendere i test più semplici da leggere.Voglio anche racchiudere le eccezioni generate dal costruttore in una RuntimeException.

Quindi, quello che voglio fare è effettivamente questo:

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

    // Mocked methods
}

Ma Java si lamenta del fatto che super non è la prima affermazione.

La mia soluzione alternativa:

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
}

È questa la soluzione migliore?Perché Java non mi consente di fare il primo?


La mia ipotesi migliore sul "perché" è che Java non vuole permettermi di avere un oggetto costruito in uno stato potenzialmente incoerente...tuttavia, nel fare una presa in giro, non mi interessa.Sembra che dovrei essere in grado di fare quanto sopra...o almeno so che quanto sopra è sicuro per il mio caso...o sembra che dovrebbe esserlo comunque.

Sto sovrascrivendo tutti i metodi che utilizzo dalla classe testata, quindi non vi è alcun rischio di utilizzare variabili non inizializzate.

È stato utile?

Soluzione

Sfortunatamente, i compilatori non possono lavorare su principi teorici e, anche se potresti sapere che è sicuro nel tuo caso, se lo consentissero, dovrebbe essere sicuro per tutti i casi.

In altre parole, il compilatore non ferma solo te, ma ferma tutti, compresi tutti quelli che non sanno che non è sicuro e necessita di una gestione speciale.Probabilmente ci sono anche altre ragioni per questo, poiché tutte le lingue di solito hanno dei modi per farlo pericoloso cose se si sa come affrontarle.

In C# .NET esistono disposizioni simili e l'unico modo per dichiarare un costruttore che chiama un costruttore di base è questo:

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

così facendo, il costruttore base verrà chiamato prima del corpo del costruttore e non è possibile modificare quest'ordine.

Altri suggerimenti

Viene fatto per evitare che qualcuno ne crei uno nuovo SecurityManager oggetto da codice non attendibile.

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

So che è una vecchia domanda, ma mi è piaciuta e, come tale, ho deciso di darle una risposta mia.Forse la mia comprensione del motivo per cui ciò non può essere fatto contribuirà alla discussione e ai futuri lettori della tua interessante domanda.

Vorrei iniziare con un esempio di costruzione di un oggetto fallita.

Definiamo una classe A, tale che:

class A {
   private String a = "A";

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

Supponiamo ora di voler creare un oggetto di tipo A in a try...catch bloccare.

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

Evidentemente l'output di questo codice sarà: null.

Perché Java non restituisce una versione parzialmente costruita di A?Dopotutto, nel momento in cui il costruttore fallisce, l'oggetto's name il campo è già stato inizializzato, giusto?

Bene, Java non può restituire una versione parzialmente costruita di A perché l'oggetto non è stato creato correttamente.L'oggetto è in uno stato incoerente e pertanto viene scartato da Java.La tua variabile A non è nemmeno inizializzata, viene mantenuta come nulla.

Ora, come sai, per costruire completamente un nuovo oggetto, tutte le sue super classi devono prima essere inizializzate.Se una delle superclassi non venisse eseguita, quale sarebbe lo stato finale dell'oggetto?È impossibile determinarlo.

Guarda questo esempio più elaborato

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

Quando il costruttore di C viene richiamato se si verifica un'eccezione durante l'inizializzazione B, quale sarebbe il valore della finale int variabile b?

In quanto tale, l'oggetto C non può essere creato, è fasullo, è spazzatura, non è completamente inizializzato.

Per me, questo spiega perché il tuo codice è illegale.

Non posso presumere di avere una profonda conoscenza degli interni di Java, ma mi risulta che, quando un compilatore deve istanziare una classe derivata, deve prima creare la base (e la sua base prima (...)) e poi applicare le estensioni realizzate nella sottoclasse.

Quindi non c'è nemmeno il pericolo di variabili non iniziate o qualcosa del genere.Quando provi a fare qualcosa nel costruttore della sottoclasse Prima la classe base' costruttore, stai sostanzialmente chiedendo al compilatore di estendere un'istanza di oggetto base che non esiste ancora.

Modifica: nel tuo caso La mia classe diventa l'oggetto base e MyClassMock è una sottoclasse.

Non so come Java sia implementato internamente, ma se il costruttore della superclasse genera un'eccezione, non esiste un'istanza della classe che estendi.Sarebbe impossibile chiamare il toString() O equals() metodi, ad esempio, poiché nella maggior parte dei casi sono ereditati.

Java può consentire un try/catch attorno alla chiamata super() nel costruttore se 1.si sovrascrivono TUTTI i metodi delle superclassi e 2.non usi la clausola super.XXX(), ma mi sembra tutto troppo complicato.

So che questa domanda ha numerose risposte, ma vorrei dare la mia piccola curiosità sul motivo per cui ciò non sarebbe consentito, in particolare per rispondere al motivo per cui Java non ti consente di farlo.Quindi ecco qua...

Ora, tienilo a mente super() deve essere chiamato prima di ogni altra cosa nel costruttore di una sottoclasse, quindi, se lo hai usato try E catch blocchi intorno al tuo super() call, i blocchi dovrebbero assomigliare a questo:

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

Se super()fails in theTentativoblock, it HAS to be executed first in thepresablock, so thatsuperruns before anything in your subclasss costruttore.Questo ti lascia con lo stesso problema che avevi all'inizio:se viene lanciata un'eccezione, non viene rilevata.(In questo caso viene semplicemente lanciato di nuovo nel blocco catch.)

Ora, il codice sopra non è in alcun modo consentito neanche da Java.Questo codice potrebbe eseguire metà della prima super chiamata e poi richiamarla di nuovo, il che potrebbe causare alcuni problemi con alcune super classi.

Ora, il motivo per cui Java non ti consente di generare un'eccezione Invece di chiamare super() è perché l'eccezione potrebbe essere rilevata da qualche altra parte e il programma continuerebbe senza chiamare super() sul tuo oggetto sottoclasse, e forse perché l'eccezione potrebbe prendere il tuo oggetto come parametro e provare a modificare il valore delle variabili di istanza ereditate, che non sarebbero state ancora inizializzate.

Un modo per aggirare il problema è chiamare una funzione statica privata.Il try-catch può quindi essere inserito nel corpo della funzione.

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);
     }
  }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top