¿Puedo llamar a métodos en constructor en Java?


Tengo una situación, en la que quiero leer el archivo de configuración solo una vez, cuando la clase es instanciada.

Supongamos que tengo un método llamado readConfig(), que lee la configuración y la coloca en un objeto Map. Cuando se requiere que el programa use el valor de configuración, lee el objeto con su clave de definición. Quiero saber que constructor llama sólo una vez que es ciclo de vida. ¿Puedo poner mi método readConfig() en constructor, lo que me daría el beneficio de una llamada de tiempo o hay otro mecanismo para hacer eso?

Author: trojanfoe, 2011-03-08

7 answers

Mejor diseño sería

public static YourObject getMyObject(File configFile){
    //process and create an object configure it and return it
}
 22
Author: Jigar Joshi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-08 09:30:44

Puedes: para esto están los constructores. También deja claro que el objeto nunca se construye en un estado desconocido (sin configuración cargada).

No debería : llamar al método de instancia en constructor es peligroso porque el objeto aún no está completamente inicializado (esto se aplica principalmente a los métodos que se pueden sobrescribir). También se sabe que el procesamiento complejo en constructor tiene un impacto negativo en la capacidad de prueba.

 49
Author: Tomasz Nurkiewicz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-30 01:23:58

El constructor se llama solo una vez, por lo que puede hacer de forma segura lo que desea, sin embargo, la desventaja de llamar a los métodos desde dentro del constructor, en lugar de directamente, es que no obtiene retroalimentación directa si el método falla. Esto se vuelve más difícil cuantos más métodos llame.

Una solución es proporcionar métodos que puede llamar para consultar el 'estado' del objeto una vez que se ha construido. Por ejemplo, el método isConfigOK() se puede usar para ver si la operación de lectura de configuración fue OK.

Otra solución es lanzar excepciones en el constructor en caso de fallo, pero realmente depende de lo 'fatal' que sean estos fallos.

class A
{
    Map <String,String> config = null;
    public A()
    {
        readConfig();
    }

    protected boolean readConfig()
    {
        ...
    }

    public boolean isConfigOK()
    {
        // Check config here
        return true;
    }
};
 2
Author: trojanfoe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-08 09:34:21

Patrón único

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. 
    }
}
 1
Author: Peeter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-07 05:27:25

Puedes. Pero al colocar esto en el constructor, está haciendo que su objeto sea difícil de probar.

En lugar de eso deberías:

  • proporcionar la configuración con un setter
  • tienen un método init() separado

Los marcos de inyección de dependencias le ofrecen estas opciones.

public class ConfigurableObject {
   private Map<String, String> configuration;
   public ConfigurableObject() {

   }

   public void setConfiguration(..) {
       //...simply set the configuration
   }
}

Un ejemplo de la 2a opción (mejor utilizada cuando el objeto es administrado por un contenedor):

public class ConfigurableObject {
   private File configFile;
   private Map<String, String> configuration;
   public ConfigurableObject(File configFile) {
       this.configFile = configFile;
   }

   public void init() {
       this.configuration = parseConfig(); // implement
   }
}

Esto, por supuesto, se puede escribir simplemente teniendo el constructor

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

Pero entonces no podrá proporcionar configuraciones simuladas.

Sé que la 2a opción suena más detallada y propensa a errores (si se olvida de inicializar). Y realmente no te hará mucho daño si lo haces en un constructor. Pero hacer que su código esté más orientado a la inyección de dependencias es generalmente una buena práctica.

La 1a opción es la mejor - se puede usar tanto con el framework DI como con el manual DI.

 1
Author: Bozho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-30 10:09:49

¿por Qué no usar Static Initialization Blocks ? Detalles adicionales aquí: Bloques de Inicialización estática

 0
Author: Ahmedov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:19

¿Puedo poner mi método readConfig() en constructor?

Invocar un método no overridable en un constructor es un enfoque aceptable.
Mientras que si el método solo es utilizado por el constructor, puede preguntarse si es realmente necesario extraerlo en un método (even private).

Si elige extraer alguna lógica hecha por el constructor en un método, como para cualquier método, debe elegir un modificador de acceso que se ajuste al requisito del método, pero en este caso específico it matters further as protecting the method against the overriding of the method has to be done at risk of making the super class constructor inconsistent.

Así que debería ser private si es usado solo por los constructores(y métodos de instancia) de la clase.
De lo contrario, debería ser package-private y final si el método se reutiliza dentro del paquete o en las subclases.

Que me daría el beneficio de una sola llamada o es hay otro mecanismo para hacer eso ?

No tiene ningún beneficio o inconveniente para usar de esta manera.
No animo a realizar mucha lógica en los constructores, pero en algunos casos puede tener sentido iniciar varias cosas en un constructor.
Por ejemplo, el constructor de copia puede realizar muchas cosas.
Múltiples clases JDK ilustran eso.
Tomemos por ejemplo el constructor copy HashMap que construye un nuevo HashMap con las mismas asignaciones que el parámetro Map especificado :

public HashMap(Map<? extends K, ? extends V> m) {
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    putMapEntries(m, false);
}

final void putMapEntries(Map<? extends K, ? extends V> 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<? extends K, ? extends V> e : m.entrySet()) {
            K key = e.getKey();
            V value = e.getValue();
            putVal(hash(key), key, value, false, evict);
        }
    }
}

Extraer la lógica del mapa que se rellena en putMapEntries() es algo bueno porque permite:

  • reutilizar el método en otros contextos. Por ejemplo clone() y putAll() úsalo también
  • (menor pero interesante) dando un nombre significativo que transmite la lógica realizada
 0
Author: davidxxx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-31 20:14:23