Construtores Java
Use construtores para inicializar objetos Java — padrão, parametrizado e sobrecarregado, mais encadeamento de construtores.
Um construtor é um método especial que é executado uma vez quando um objeto é criado. Seu papel é levar a nova instância de "todos os campos com seus valores padrão" para "pronto para uso." Até agora você vinha definindo campos um por um após new Dog(); um construtor permite agrupar esse trabalho na chamada new.
Anatomia de um construtor
Um construtor parece um método, com três diferenças: ele tem o mesmo nome da classe, não tem tipo de retorno (nem mesmo void), e é chamado apenas por new:
public class Point {
int x, y;
public Point(int x, int y) { // constructor
this.x = x;
this.y = y;
}
}
Point p = new Point(3, 4); // runs the constructorA lista de argumentos vai dentro dos () após o nome da classe na expressão new. Java combina essa lista de argumentos com um construtor da classe, assim como combina uma chamada de método com um método.
O construtor padrão
Se você não declarar nenhum construtor, Java fornece à classe um construtor padrão — um construtor sem argumentos que não faz nada:
public class Empty { } // compiler generates a no-arg constructor
Empty e = new Empty(); // worksÉ assim que new Dog() funcionava nos exemplos anteriores, mesmo sem termos escrito public Dog() { ... }. No momento em que você declara qualquer construtor próprio, o construtor padrão gratuito desaparece:
public class Point {
int x, y;
public Point(int x, int y) { this.x = x; this.y = y; }
}
Point p = new Point(); // ERROR — no no-arg constructor existsSe você quiser os dois, declare os dois — veja sobrecarga abaixo.
Por que usar um construtor?
Duas razões.
1. Campos obrigatórios. Um construtor permite tornar alguns campos não opcionais. Com campo público e atribuição posterior, nada impede um chamador de criar uma Person sem name. Com um construtor Person(String name), isso é impossível.
2. Invariantes. Um construtor pode validar seus argumentos antes que o objeto exista em forma utilizável. Se um Circle exige um raio positivo, o construtor pode lançar uma exceção para um valor negativo — o objeto com defeito nunca é criado.
public class Circle {
double radius;
public Circle(double radius) {
if (radius <= 0) throw new IllegalArgumentException("radius must be > 0");
this.radius = radius;
}
}O chamador agora não pode obter um Circle com raio não positivo.
Construtores sobrecarregados
Uma classe pode ter vários construtores com listas de parâmetros diferentes, exatamente como métodos sobrecarregados:
public class Rectangle {
double width, height;
public Rectangle() { this(1, 1); }
public Rectangle(double side) { this(side, side); } // a square
public Rectangle(double w, double h) { this.width = w; this.height = h; }
}Agora estes três compilam:
Rectangle a = new Rectangle(); // 1 x 1
Rectangle b = new Rectangle(5); // 5 x 5
Rectangle c = new Rectangle(3, 4); // 3 x 4Construtores de cópia
Uma sobrecarga comum é um construtor de cópia — um que recebe outra instância da mesma classe e constrói uma cópia independente. Java não tem construtor de cópia embutido (ao contrário do C++), então você escreve o seu quando precisar:
public class Point {
int x, y;
public Point(int x, int y) { this.x = x; this.y = y; }
public Point(Point other) { this(other.x, other.y); } // copy constructor
}
Point a = new Point(3, 4);
Point b = new Point(a); // a separate Point with the same valuesComo b é um novo objeto, alterar b.x depois não afeta a. Para classes cujos campos são objetos mutáveis, copie também os campos (uma cópia profunda) se quiser que a cópia seja totalmente independente.
this(...) — encadeamento de construtores
Dentro de um construtor, this(args) chama outro construtor da mesma classe. É assim que o exemplo Rectangle acima evitou duplicar o código de atribuição de campos: os dois construtores de conveniência delegam para o completo.
Duas regras:
this(...)deve ser a primeira instrução no corpo do construtor.- Um construtor pode encadear para um outro construtor.
public Rectangle() { this(1, 1); } // ok
public Rectangle(double s) { System.out.println("hi"); this(s, s); } // ERRORA razão para a regra de "primeira instrução" é que a JVM precisa inicializar o objeto completamente apenas uma vez antes de qualquer outro código ser executado.
super(...) — chamando o pai
Quando uma classe estende outra, cada construtor ou chama explicitamente um construtor pai com super(args), ou chama implicitamente o construtor sem argumentos do pai. Cobriremos isso completamente nos capítulos de herança e palavra-chave super; por ora, saiba apenas que super(...) existe e tem a mesma regra de primeira instrução que this(...).
Construtores não podem retornar um valor
Um construtor não tem tipo de retorno — nem mesmo void. Se você escrever um:
public void Point(int x, int y) { ... } // not a constructor!…é um método comum que por acaso se chama Point. O compilador não irá avisá-lo; new Point(3, 4) então falhará ao compilar porque o verdadeiro construtor que está procurando não existe. Este é um erro de digitação surpreendentemente comum.
Ordem de inicialização
Para uma única classe, a ordem é:
- Valores padrão dos campos são atribuídos (ex.: campos
inttornam-se0). - Inicializadores de campo inline (
int count = 5;) e quaisquer blocos inicializadores de instância são executados na ordem em que aparecem. - O corpo do construtor é executado.
public class Demo {
int a = compute("a-init", 1);
int b;
{ b = compute("b-block", 2); } // instance initializer
public Demo() {
System.out.println("constructor; a=" + a + ", b=" + b);
}
static int compute(String label, int v) {
System.out.println(label);
return v;
}
}new Demo() imprime a-init, b-block, depois constructor; a=1, b=2.
Na prática, quase toda inicialização pertence ao corpo do construtor. Blocos inicializadores de instância são raros no código real; existem principalmente para situações como classes anônimas (cobertas mais adiante).
Um exemplo prático
O que vem a seguir
Dentro dos construtores você já viu this.field = field para diferenciar um parâmetro de um campo. O capítulo sobre a palavra-chave this explica o que é this e os poucos lugares onde você precisa escrevê-lo explicitamente.