web-dev-qa-db-fra.com

Puis-je appeler des méthodes dans constructeur en Java?

J'ai une situation, où je veux lire le fichier de configuration une seule fois, lorsque la classe est instanciée.

Supposons que j'ai une méthode nommée readConfig(), qui lit la configuration et la place dans un objet Map. Lorsque le programme doit utiliser la valeur de configuration, il lit l'objet avec sa clé de définition. Je veux savoir que le constructeur n'appelle qu'une fois que c'est le cycle de vie. Puis-je mettre ma méthode readConfig() dans le constructeur, ce qui me donnerait l'avantage d'appeler une fois ou existe-t-il un autre mécanisme pour le faire?

44
Sameek Mishra

Une meilleure conception serait

public static YourObject getMyObject(File configFile){
    //process and create an object configure it and return it
}
25
Jigar Joshi

Vous pouvez: c'est à cela que servent les constructeurs. Vous indiquez également clairement que l'objet n'est jamais construit dans un état inconnu (sans configuration chargée).

Vous ne devriez pas: appeler la méthode d'instance dans le constructeur est dangereux car l'objet n'est pas encore complètement initialisé (cela s'applique principalement aux méthodes qui peuvent être remplacées). Un traitement complexe dans le constructeur est également connu pour avoir un impact négatif sur la testabilité.

60

Puis-je mettre ma méthode readConfig () dans le constructeur?

L'invocation d'une méthode non redéfinissable dans un constructeur est une approche acceptable.
Bien que si la méthode n'est utilisée que par le constructeur, vous pouvez vous demander si son extraction dans une méthode (même private) est vraiment nécessaire.

Si vous choisissez d'extraire une logique effectuée par le constructeur dans une méthode, comme pour toute méthode, vous devez choisir un modificateur d'accès qui correspond à l'exigence de la méthode, mais dans ce cas spécifique, il importe davantage comme la protection de la méthode contre le le remplacement de la méthode doit être fait au risque de rendre le constructeur de la super classe incohérent.

Il doit donc être private s'il n'est utilisé que par le ou les constructeurs (et les méthodes d'instance) de la classe.
Sinon, il devrait être à la fois package-private Et final si la méthode est réutilisée à l'intérieur du package ou dans les sous-classes.

qui me ferait bénéficier d'un appel unique ou existe-t-il un autre mécanisme pour le faire?

Vous n'avez aucun avantage ou inconvénient à utiliser de cette façon.
Je n'encourage pas à effectuer beaucoup de logique dans les constructeurs mais dans certains cas, il peut être judicieux d'initier plusieurs choses dans un constructeur.
Par exemple, le constructeur de copie peut effectuer beaucoup de choses.
Plusieurs classes JDK illustrent cela.
Prenons par exemple le constructeur de copie HashMap qui construit un nouveau HashMap avec les mêmes mappages que le paramètre Map spécifié:

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

Extraire la logique de la carte peuplée dans putMapEntries() est une bonne chose car elle permet:

  • réutiliser la méthode dans d'autres contextes. Par exemple clone() et putAll() l'utilise aussi
  • (mineur mais intéressant) donnant un nom significatif qui transmet la logique exécutée
7
davidxxx

Le constructeur n'est appelé qu'une seule fois, vous pouvez donc faire ce que vous voulez en toute sécurité, mais l'inconvénient d'appeler des méthodes à partir du constructeur, plutôt que directement, est que vous n'obtenez pas de rétroaction directe si la méthode échoue. Cela devient plus difficile plus vous appelez de méthodes.

Une solution consiste à fournir des méthodes que vous pouvez appeler pour interroger la "santé" de l'objet une fois qu'il a été construit. Par exemple, la méthode isConfigOK() peut être utilisée pour voir si l'opération de lecture de configuration était OK.

Une autre solution consiste à lever des exceptions dans le constructeur en cas d'échec, mais cela dépend vraiment de la gravité de ces échecs.

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

    protected boolean readConfig()
    {
        ...
    }

    public boolean isConfigOK()
    {
        // Check config here
        return true;
    }
};
2
trojanfoe

Vous pouvez. Mais en plaçant cela dans le constructeur, vous rendez votre objet difficile à tester.

Au lieu de cela, vous devez:

  • fournir la configuration avec un setter
  • avoir une méthode init() distincte

Les frameworks d'injection de dépendances vous offrent ces options.

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

   }

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

Un exemple de la 2e option (mieux utilisée lorsque l'objet est géré par un conteneur):

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
   }
}

Bien sûr, cela peut être écrit en ayant simplement le constructeur

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

Mais vous ne pourrez alors pas fournir de configurations fictives.

Je sais que la 2e option semble plus verbeuse et sujette à erreur (si vous oubliez d'initialiser). Et cela ne vous fera pas vraiment mal si vous le faites chez un constructeur. Mais rendre votre code plus orienté vers l'injection de dépendances est généralement une bonne pratique.

La 1ère option est la meilleure - elle peut être utilisée avec le cadre DI et avec DI manuel.

1
Bozho

Motif 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. 
    }
}
1
Peeter

Pourquoi ne pas utiliser Static Initialization Blocks? Détails supplémentaires ici: Blocs d'initialisation statiques

0
Ahmedov