Posso chiamare metodi in costruttore in Java?

Ho una situazione, in cui voglio leggere il file di configurazione solo una volta, quando la class viene istanziata.

Supponiamo che abbia un metodo chiamato readConfig() , che legge la configurazione e la inserisce in un object Map . Quando il programma è richiesto per utilizzare il valore di configurazione legge l’object con la sua chiave di definizione. Voglio sapere che le chiamate del costruttore solo una volta che è il ciclo di vita. Posso mettere il mio metodo readConfig() in costruttore, che mi darebbe un vantaggio di una chiamata in un momento o esiste un altro meccanismo per farlo?

Il design migliore sarebbe

 public static YourObject getMyObject(File configFile){ //process and create an object configure it and return it } 
  • Modello di progettazione di fabbrica

Puoi : questo è ciò che i costruttori sono per. Inoltre chiarisci che l’object non viene mai costruito in uno stato sconosciuto (senza configurazione caricata).

Non dovresti : chiamare il metodo di istanza nel costruttore è pericoloso perché l’object non è ancora completamente inizializzato (questo vale soprattutto per i metodi di quelli che possono essere sovrascritti). Anche l’elaborazione complessa nel costruttore ha un impatto negativo sulla testabilità.

Il costruttore viene chiamato una sola volta, quindi puoi tranquillamente fare ciò che vuoi, tuttavia lo svantaggio di chiamare i metodi dal costruttore, piuttosto che direttamente, è che non ottieni un feedback diretto se il metodo fallisce. Questo diventa più difficile più metodi tu chiami.

Una soluzione consiste nel fornire metodi che è ansible chiamare per interrogare la ‘salute’ dell’object una volta che è stato creato. Ad esempio, è ansible utilizzare il metodo isConfigOK() per vedere se l’operazione di lettura della configurazione era OK.

Un’altra soluzione consiste nel generare eccezioni nel costruttore in caso di errore, ma in realtà dipende da quanto “fatali” siano questi guasti.

 class A { Map  config = null; public A() { readConfig(); } protected boolean readConfig() { ... } public boolean isConfigOK() { // Check config here return true; } }; 

Modello Singleton

 public class MyClass() { private static MyClass instance = null; /** * Get instance of my class, Singleton **/ public static MyClass getInstance() { if(instance == null) { instance = new MyClass(); } return instance; } /** * Private constructor */ private MyClass() { //This will only be called once, by calling getInstanse() method. } } 

Puoi. Ma inserendolo nel costruttore si sta rendendo difficile il test dell’object.

Invece dovresti:

  • fornire la configurazione con un setter
  • avere un metodo init() separato

I quadri di iniezione delle dipendenze offrono queste opzioni.

 public class ConfigurableObject { private Map configuration; public ConfigurableObject() { } public void setConfiguration(..) { //...simply set the configuration } } 

Un esempio della seconda opzione (utilizzata al meglio quando l’object è gestito da un contenitore):

 public class ConfigurableObject { private File configFile; private Map configuration; public ConfigurableObject(File configFile) { this.configFile = configFile; } public void init() { this.configuration = parseConfig(); // implement } } 

Questo, ovviamente, può essere scritto semplicemente avendo il costruttore

 public ConfigurableObject(File configfile) { this.configuration = parseConfig(configFile); } 

Ma allora non sarai in grado di fornire configurazioni di simulazione.

So che il secondo opttion sembra più prolisso e incline all’errore (se si dimentica di inizializzare). E non ti farà molto male se lo fai in un costruttore. Ma rendere il tuo codice più dipendente dall’iniezione è generalmente una buona pratica.

La prima opzione è la migliore: può essere utilizzata sia con DI framework che con DI manuale.

Perché non usare i Static Initialization Blocks ? Ulteriori dettagli qui: Blocchi di inizializzazione statici

Posso mettere il mio metodo readConfig () in costruttore?

Invocare un metodo non sovrascrivibile in un costruttore è un approccio accettabile.
Mentre se il metodo viene utilizzato solo dal costruttore, ci si potrebbe chiedere se è necessario estrarlo in un metodo (anche private ).

Se si sceglie di estrarre una logica eseguita dal costruttore in un metodo, come per qualsiasi metodo, è necessario scegliere un modificatore di accesso che si adatti al requisito del metodo, ma in questo caso specifico importa ulteriormente come protezione del metodo rispetto all’override del metodo deve essere fatto a rischio di rendere incoerente il costruttore super-class .

Quindi dovrebbe essere private se è usato solo dal costruttore (s) (e dai metodi di istanza) della class.
Altrimenti dovrebbe essere sia package-private che final se il metodo viene riutilizzato all’interno del pacchetto o nelle sottoclassi.

quale mi darebbe beneficio di una volta che chiama o c’è un altro meccanismo per farlo?

Non hai alcun vantaggio o svantaggio di utilizzare in questo modo.
Non incoraggio a eseguire molta logica nei costruttori, ma in alcuni casi può avere senso iniziare più cose in un costruttore.
Ad esempio, il costruttore di copie potrebbe eseguire molte cose.
Molteplici classi JDK lo dimostrano.
Prendiamo ad esempio il HashMap copie HashMap che costruisce una nuova HashMap con gli stessi mapping del parametro Map specificato:

 public HashMap(Map m) { this.loadFactor = DEFAULT_LOAD_FACTOR; putMapEntries(m, false); } final void putMapEntries(Map m, boolean evict) { int s = m.size(); if (s > 0) { if (table == null) { // pre-size float ft = ((float)s / loadFactor) + 1.0F; int t = ((ft < (float)MAXIMUM_CAPACITY) ? (int)ft : MAXIMUM_CAPACITY); if (t > threshold) threshold = tableSizeFor(t); } else if (s > threshold) resize(); for (Map.Entry e : m.entrySet()) { K key = e.getKey(); V value = e.getValue(); putVal(hash(key), key, value, false, evict); } } } 

Estrarre la logica della mappa che popola in putMapEntries() è una buona cosa perché permette:

  • riutilizzare il metodo in altri contesti. Ad esempio, anche clone() e putAll() usano
  • (minore ma interessante) dando un nome significativo che trasmette la logica eseguita