Desafio Java
Ponto imutável com cópia defensiva
Enunciado
Crie uma classe imutável Ponto com coordenadas x e y (private final double). Ela deve ter um método mover(double dx, double dy) que retorna um novo Ponto em vez de alterar o atual. Adicione também um método distanciaA(Ponto outro) que calcula a distância euclidiana.
Requisitos
xeysãoprivate final double.- Não existe setter;
moverretorna um novoPonto. distanciaA(Ponto outro)usaMath.hypotouMath.sqrt.- O
maincria um ponto, gera outro viamovere imprime as coordenadas e a distância.
Código inicial
public class Ponto {
// TODO: atributos private final
public Ponto(double x, double y) {
// TODO: atribua
}
public double getX() { return x; }
public double getY() { return y; }
// TODO: mover e distanciaA
public static void main(String[] args) {
// TODO: demonstre
}
}
Saída esperada
p1 = (0.0, 0.0)
p2 = (3.0, 4.0)
distancia = 5.0
Ver dica
Math.hypot(dx, dy) calcula a hipotenusa sem risco de overflow e já é bem precisa.
Mostrar solução
public class Ponto {
private final double x; // final: imutável após o construtor
private final double y;
public Ponto(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() { return x; }
public double getY() { return y; }
// Não altera this; devolve um novo Ponto
public Ponto mover(double dx, double dy) {
return new Ponto(this.x + dx, this.y + dy);
}
// Distância euclidiana até outro ponto
public double distanciaA(Ponto outro) {
return Math.hypot(this.x - outro.x, this.y - outro.y);
}
@Override
public String toString() {
return "(" + x + ", " + y + ")";
}
public static void main(String[] args) {
Ponto p1 = new Ponto(0, 0);
Ponto p2 = p1.mover(3, 4); // p1 continua (0,0)
System.out.println("p1 = " + p1);
System.out.println("p2 = " + p2);
System.out.println("distancia = " + p1.distanciaA(p2));
}
}
Passo a passo
xeysãoprivate final, então só podem ser atribuídos no construtor e nunca mais mudam.- Não existe setter: a única forma de "mudar" um ponto é criar outro.
mover(dx, dy)calculax + dxey + dye devolve umnew Ponto, deixando o original intacto.distanciaA(outro)usaMath.hypotpara calcular a hipotenusa entre as diferenças de coordenadas.- Sobrescrevemos
toStringsó para deixar a saída legível. - No
main,p1permanece(0.0, 0.0)mesmo depois dep1.mover(3, 4), porque o resultado foi guardado emp2.
Por que funciona
Imutabilidade combina private final, ausência de setters e operações que retornam novos objetos. Como o estado nunca muda, não há risco de um método alterar o ponto "por baixo dos panos" de outro código, e a classe é naturalmente segura para uso concorrente. Encapsular aqui significa não expor x e y para escrita, apenas para leitura.
Erros comuns
- Declarar
private double x;semfinale criar um setter: a classe deixa de ser imutável. - Fazer
moveralterarthis.x += dx;e retornarthis: o objeto original muda, quebrando a promessa de imutabilidade. - Expor arrays ou listas internas diretamente em getters: mesmo com
final, o conteúdo pode ser alterado. Nesse caso, devolva uma cópia (clone()ouList.copyOf(...)). - Esquecer de marcar a classe como
finalse ela puder ser herdada: uma subclasse poderia adicionar estado mutável.
Outra forma de resolver
Em vez de mover, você pode usar um método estático fábrica que descreve a operação:
public static Ponto transladado(Ponto p, double dx, double dy) {
return new Ponto(p.x + dx, p.y + dy);
}
É preferível quando a operação não pertence conceitualmente a um único ponto, mas sim a uma transformação sobre pontos.
Saída esperada
p1 = (0.0, 0.0)
p2 = (3.0, 4.0)
distancia = 5.0