2013-08-09 16 views
21

Scusa qualsiasi errore di sintassi minore o quant'altro, sto vivendo questo con un modulo Jitsi e non conosco molto bene Java per confermare cosa sta succedendo e perché e come dovrebbe essere corretto.Metodo di chiamata dal costruttore

public abstract class A 
{ 
    public A() 
    { 
    this.load(); 
    } 

    protected void load() 
    { 

    } 
} 

public class B extends A 
{ 
    private String testString = null; 

    public B() 
    { 
    super(); 
    } 

    @Override 
    protected void load() 
    { 
    testString = "test"; 
    } 
} 

L'applicazione sta facendo questo quando si crea un'istanza della classe B utilizzando una classe di carico con il metodo nome:

  • chiede carico override() in classe B
  • di inizializzare variabili (chiamate " private string testString = null "secondo il debugger), annullandoli.

È previsto questo comportamento Java? Cosa potrebbe causare questo? È un'applicazione Java 1.6 in esecuzione sul JDK 1.7.

risposta

55

È previsto questo comportamento Java?

Sì.

Cosa potrebbe causare questo?

Invocazione di metodo non finalizzato a override nel costruttore di classi superfinali.

Vediamo cosa succede step-by-step:

  • Si crea un'istanza di B.
  • B() chiama il costruttore super-classe - A(), per inizializzare i membri della classe super.
  • A() invoca ora un metodo non finale che viene sovrascritto nella classe B come parte dell'inizializzazione.
  • Poiché l'istanza nel contesto è della classe B, il metodo load() richiamato è della classe B.
  • load() inizializza il campo di istanza della classe B - testString.
  • Il costruttore di super-classe finisce lavoro, e ritorna (Supponendo che il concatenamento di costruttore fino Object classe sono stati finiti)
  • Il costruttore B() inizia l'esecuzione di più, l'inizializzazione di un proprio membro.
  • Ora, come parte del processo di inizializzazione, B sovrascrive il precedente valore scritto in testString e lo reinizializza su null.

Morale: Mai chiamare un metodo pubblico non definitiva di una classe non-finale in esso di costruttore.

+21

+1 per il morale – yshavit

+1

Grande risposta. Ho appena imparato molto. Grazie. –

+0

Fantastico, grazie. Analizzerò come funzionano le altre parti del progetto, devono farlo nel modo giusto e questo è l'outlier, quindi seguirò il resto del comportamento dell'applicazione. – StrangeWill

5

Questo è un modello di problemi comune con inizializzazione su costruzione e può essere trovato frequentemente nel codice di infrastruttura & DAO fatti in casa.

L'assegnazione a "null" non è necessaria & può essere rimosso.

Se ciò non è sufficiente come patch rapida, quindi: sposta tutti i init post-costruzione su un metodo separato e avvolgi tutto in uno pseudo-costruttore "metodo statico".

E se stai facendo roba DAO, è molto buono distinguere tra "caricamento" e "creazione", poiché si tratta di istanze completamente diverse. Definire i metodi separati "costruttore statico" & forse separati interni, per questi.

abstract public class A { 
    protected void initAfterCreate() {} 
} 

public class B { 

    @Override 
    protected void initAfterCreate() { 
     this.testString = "test"; 
    } 

    // static constructors; 
    //  --   
    static public B createB() { 
     B result = new B(); 
     result.initAfterCreate(); 
    } 
} 

Dimostrando carico/creare di separazione per un DAO:

public class Order { 
    protected int id; 
    protected boolean dbExists; 

    static public load (int id) { 
     Order result = new Order(id, true); 
     // populate from SQL query.. 
     return result; 
    } 
    static public create() { 
     // allocate a key. 
     int id = KeyAlloc.allocate("Order"); 
     Order result = new Order(id, false); 
    } 

    // internal constructor; not for external access. 
    // 
    protected Order (int id, boolean dbExists) { 
     this.id = id; 
     this.dbExists = dbExists; 
    } 
} 
+0

load() è un metodo tipicamente utilizzato dai moduli per estrarre la loro configurazione dai file .properties, ho toccato base con la mailing list di Jitsi dev e vedremo quale è l'approccio consigliato per mantenere coerenza nel codice (non tutti i moduli caricano le proprietà e mi sono stancato di guardare ... devo comunque inserire un bug nella loro lista). – StrangeWill

+0

Ok, quindi non è roba DAO. Ciò renderà più semplice - dovrebbe essere solo un percorso di inizializzazione allora. 'initAfterCreate()' è l'unica soluzione fondamentalmente affidabile, e preferibilmente lo avvolge in un metodo statico di fabbrica. –

+0

Perché un downvote? Condivisione di molte esperienze reali sui nodi dell'iniziativa: le circostanze pratiche in cui si presentano e schemi efficaci ("costruttore statico") per risolverli. –

Problemi correlati