Posso chamar methods no construtor em Java?

Eu tenho situação, onde eu quero ler o arquivo de configuração apenas uma vez, quando a class é instanciada.

Suponha que eu tenha um método chamado readConfig() , que leia a configuração e coloque-o em um object Map . Quando o programa é necessário para usar o valor de configuração, ele lê o object com sua chave de definição. Eu quero saber que o construtor chama apenas uma vez que é o ciclo de vida. Posso colocar meu método readConfig() em construtor, o que me dava o benefício de ligar uma vez ou há outro mecanismo para fazer isso?

Melhor design seria

 public static YourObject getMyObject(File configFile){ //process and create an object configure it and return it } 
  • Padrão de design de fábrica

Você pode : é para isso que os construtores são. Também você deixa claro que o object nunca é construído em um estado desconhecido (sem configuração carregada).

Você não deve : chamar o método de instância no construtor é perigoso porque o object ainda não está totalmente inicializado (isso se aplica principalmente a methods que podem ser substituídos). Também é sabido que o processamento complexo no construtor tem um impacto negativo na testabilidade.

O construtor é chamado apenas uma vez, portanto, você pode fazer com segurança o que quiser, mas a desvantagem de chamar methods de dentro do construtor, em vez de diretamente, é que você não recebe feedback direto se o método falhar. Isso fica mais difícil quanto mais methods você chama.

Uma solução é fornecer methods que você possa chamar para consultar a “integridade” do object, uma vez que ele tenha sido construído. Por exemplo, o método isConfigOK() pode ser usado para ver se a operação de leitura de configuração foi OK.

Outra solução é lançar exceções no construtor em caso de falha, mas isso realmente depende de quão “fatais” são essas falhas.

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

Padrão 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. } } 

Você pode. Mas, colocando isso no construtor, você está dificultando o teste do object.

Em vez disso você deve:

  • fornecer a configuração com um setter
  • tem um método init() separado

Estruturas de injeção de dependência fornecem essas opções.

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

Um exemplo da segunda opção (melhor usada quando o object é gerenciado por um contêiner):

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

Isso, claro, pode ser escrito apenas com o construtor

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

Mas você não poderá fornecer configurações simuladas.

Eu sei que a segunda opção soa mais detalhada e propensa a erros (se você esquecer de inicializar). E isso não vai te machucar muito se você fizer isso em um construtor. Mas tornar seu código mais orientado para injeção de dependência geralmente é uma boa prática.

A primeira opção é a melhor – ela pode ser usada com a estrutura DI e com DI manual.

Por que não usar Static Initialization Blocks ? Detalhes adicionais aqui: Blocos de boot estática

Posso colocar meu método readConfig () em construtor?

Invocar um método não substituível em um construtor é uma abordagem aceitável.
Enquanto se o método é usado apenas pelo construtor, você pode se perguntar se extraí-lo em um método (mesmo private ) é realmente necessário.

Se você escolher extrair alguma lógica feita pelo construtor em um método, como para qualquer método, você deve escolher um modificador de access que se ajuste ao requisito do método, mas neste caso específico, é mais importante proteger o método contra a substituição do método. tem que ser feito em risco de tornar o construtor da superclass inconsistente .

Portanto, ele deve ser private se for usado apenas pelo (s) construtor (es) (e methods de instância) da class.
Caso contrário, deve ser tanto package-private quanto final se o método for reutilizado dentro do pacote ou nas subclasss.

qual me daria o benefício de ligar uma vez ou há outro mecanismo para fazer isso?

Você não tem nenhum benefício ou desvantagem para usar dessa maneira.
Eu não encorajo a realizar muita lógica em construtores, mas em alguns casos pode fazer sentido iniciar várias coisas em um construtor.
Por exemplo, o construtor de cópia pode executar muitas coisas.
Várias classs JDK ilustram isso.
Tomemos por exemplo o construtor de cópia HashMap que constrói um novo HashMap com os mesmos mapeamentos que o parâmetro Map especificado:

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

Extrair a lógica do mapa preenchido em putMapEntries() é bom porque permite:

  • reutilizando o método em outros contextos. Por exemplo, clone() e putAll() usam também
  • (menor, mas interessante), dando um nome significativo que transmite a lógica executada