java-constructores-this-sobrecarga

Constructores y this en Java: inicializar objetos

  • 4 min

Un constructor es el bloque que inicializa una nueva instancia de una clase.

En el artículo anterior creamos nuestros primeros objetos Robot, pero había algo que “olía mal” en el código.

Teníamos que hacer esto:

Robot r1 = new Robot(); // Nace vacío (nombre null, batería 0)
// <--- Aquí el robot existe pero es inválido/inútil
r1.nombre = "Wall-E";   // Lo arreglamos manualmente
r1.nivelBateria = 100;
Copied!

Esto es peligroso. ¿Qué pasa si a un programador despistado se le olvida poner las líneas de configuración? Que tendremos un Robot sin nombre paseando por nuestro sistema, y cuando intente hablar, el programa explotará.

Para solucionar esto, Java utiliza los Constructores. Un mecanismo para obligar a que el objeto nazca ya configurado y listo para usarse.

¿Qué es un constructor?

Un constructor es un bloque de código especial que se ejecuta automáticamente en el momento en que haces new.

Su misión es inicializar el objeto. Es decir, dar valores iniciales a los atributos para asegurar que el objeto tiene un estado válido desde el milisegundo cero.

Sintaxis

El constructor tiene dos reglas de oro:

  1. Se llama exactamente igual que la clase.
  2. No tiene tipo de retorno (ni siquiera void).
public class Robot {
    String nombre;
    int nivelBateria;

    // ESTE ES EL CONSTRUCTOR
    public Robot(String nombreInicial, int bateriaInicial) {
        nombre = nombreInicial;
        nivelBateria = bateriaInicial;
    }
}
Copied!

Ahora, al crear el objeto, Java nos obliga a pasar los datos. Ya no podemos crear robots vacíos por error.

// Robot r = new Robot(); // ¡ERROR DE COMPILACIÓN! Java nos exige los parámetros.

Robot r1 = new Robot("Wall-E", 100); // Nace perfecto.
Copied!

La palabra clave this

En el ejemplo anterior he usado trucos sucios como llamar al parámetro nombreInicial para que no coincida con el atributo nombre.

En la vida real, queremos que los parámetros se llamen igual que los atributos (nombre, bateria). Pero si hacemos esto, tenemos un problema de ambigüedad (Shadowing):

public Robot(String nombre) {
    nombre = nombre; // ¿Quién es quién?
    // Aquí Java asume que ambas son la variable local (el parámetro).
    // El atributo de la clase NO se modifica.
}
Copied!

Para romper esta ambigüedad usamos this. this es una referencia que apunta a la propia instancia (al propio objeto que está ejecutando el código).

Léelo como: “En mi atributo nombre (this.nombre), guarda el valor del parámetro nombre”.

public Robot(String nombre, int nivelBateria) {
    this.nombre = nombre;            // Correcto
    this.nivelBateria = nivelBateria; // Correcto
}
Copied!

El constructor por defecto (el regalo invisible)

Si tú no escribes ningún constructor en tu clase, Java es amable e inserta uno invisible por ti: el Constructor por Defecto (sin parámetros y vacío).

// Java inserta esto si tú no pones nada:
public Robot() { }
Copied!

Por eso en el artículo anterior podíamos hacer new Robot().

En el momento en que tú escribes cualquier constructor (aunque sea uno con parámetros), Java dice: “Ah, ya sabes lo que haces, retiro mi ayuda”. El constructor por defecto desaparece.

Si quieres seguir pudiendo crear objetos vacíos (new Robot()), tendrás que escribir explícitamente el constructor vacío tú mismo.

Sobrecarga de constructores (overloading)

A veces queremos flexibilidad. Quizás queremos crear un Robot dando todos los datos, o quizás queremos crear un Robot dando solo el nombre y asumiendo que la batería empieza al 100%.

Java permite tener múltiples constructores en la misma clase, siempre que tengan diferente lista de parámetros. Esto se llama Sobrecarga.

public class Robot {
    String nombre;
    int nivelBateria;

    // Opción A: Dando todo
    public Robot(String nombre, int nivelBateria) {
        this.nombre = nombre;
        this.nivelBateria = nivelBateria;
    }

    // Opción B: Dando solo el nombre (Batería por defecto a 100)
    public Robot(String nombre) {
        this.nombre = nombre;
        this.nivelBateria = 100; // Valor por defecto
    }
}
Copied!

Ahora el usuario de tu clase puede elegir:

Robot r1 = new Robot("R2-D2", 50); // Usa el constructor A
Robot r2 = new Robot("C-3PO");     // Usa el constructor B
Copied!

Constructor chaining (this(...))

En el ejemplo anterior, hemos repetido código (this.nombre = nombre). En clases grandes, repetir lógica de inicialización es mala práctica.

Podemos llamar a un constructor desde otro constructor usando this(...). Regla: Esta llamada debe ser la primera línea del constructor.

// Constructor principal (el que hace el trabajo)
public Robot(String nombre, int nivelBateria) {
    this.nombre = nombre;
    this.nivelBateria = nivelBateria;
}

// Constructor auxiliar (delega en el principal)
public Robot(String nombre) {
    // "Llama al constructor de arriba pasando 'nombre' y un 100 fijo"
    this(nombre, 100);
}
Copied!

Esto es elegante y profesional. Centralizas la lógica de inicialización en un único punto.