CodeGym/Blog Java/Random-ES/Constructores Java
Autor
Jesse Haniel
Lead Software Architect at Tribunal de Justiça da Paraíba

Constructores Java

Publicado en el grupo Random-ES
¡Hola! Hoy consideraremos un tema muy importante que concierne a nuestros objetos. Sin exagerar, ¡podemos decir que usará este tema en la vida real todos los días! Estamos hablando de constructores de Java. Esta puede ser la primera vez que escucha este término, pero en realidad ya ha usado constructores. Simplemente no te diste cuenta :) Nos convencemos de esto más tarde.

¿Qué demonios son los constructores y por qué son necesarios?

Consideremos dos ejemplos.
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car bugatti = new Car();
       bugatti.model = "Bugatti Veyron";
       bugatti.maxSpeed = 378;

   }
}
Creamos nuestro coche, y fijamos su modelo y velocidad máxima. Pero el objeto Car obviamente no tendría 2 campos en un proyecto real. Por ejemplo, ¡podría tener 16 campos!
public class Car {

   String model;// model
   int maxSpeed;// maximum speed
   int wheels;// wheel width
   double engineVolume;// engine volume
   String color;// color
   int productionYear;// production year
   String ownerFirstName;// first name of owner
   String ownerLastName;// last name of owner
   long price;// price
   boolean isNew;// flag indicating whether car is new
   int seatsInTheCar;// number of seats in the car
   String cabinMaterial;// interior material
   boolean insurance;// flag indicating whether car is insured
   String manufacturerCountry;// manufacturer country
   int trunkVolume;// size of the trunk
   int accelerationTo100km;// how long it takes to accelerate to 100 km/h (in seconds)


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.productionYear = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.seatsInTheCar = 2;
       bugatti.maxSpeed = 378;
       bugatti.model = "Bugatti Veyron";

   }

}
Hemos creado un nuevo objeto Car . Hay un problema: tenemos 16 campos, ¡pero solo inicializamos 12 ! ¡Mira el código ahora e intenta encontrar los campos que olvidamos! No es tan fácil, ¿eh? En esta situación, un programador puede cometer un error fácilmente y fallar al inicializar algún campo. Como resultado, el programa se comportará incorrectamente:
public class Car {

   String model;// model
   int maxSpeed;// maximum speed
   int wheels;// wheel width
   double engineVolume;// engine volume
   String color;// color
   int productionYear;// production year
   String ownerFirstName;// first name of owner
   String ownerLastName;// last name of owner
   long price;// price
   boolean isNew;// flag indicating whether car is new
   int seatsInTheCar;// number of seats in the car
   String cabinMaterial;// interior material
   boolean insurance;// flag indicating whether car is insured
   String manufacturerCountry;// manufacturer country
   int trunkVolume;// size of the trunk
   int accelerationTo100km;// how long it takes to accelerate to 100 km/h (in seconds)


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.productionYear = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.seatsInTheCar = 2;
       bugatti.maxSpeed = 378;
       bugatti.model = "Bugatti Veyron";

       System.out.println("Model: Bugatti Veyron. Engine volume: " + bugatti.engineVolume + ". Trunk volume: " + bugatti.trunkVolume + ". Cabin material: " + bugatti.cabinMaterial +
       ". Wheel width: " + bugatti.wheels + ". Purchased in 2018 by Mr. " + bugatti.ownerLastName);

   }

}
Salida de consola: Modelo: Bugatti Veyron. Volumen del motor: 6.3. Volumen maletero: 0. Material cabina: nulo. Ancho de la rueda: 0. Comprado en 2018 por el Sr. nulo ¡ A su comprador, que renunció a $ 2 millones por el automóvil, obviamente no le gustará que lo llamen " Sr. nulo "! Pero en serio, la conclusión es que nuestro programa creó un objeto incorrectamente: un automóvil con un ancho de rueda de 0 (es decir, sin ruedas), un baúl que falta, una cabina hecha de un material desconocido y, sobre todo, un propietario indefinido. . ¡Solo puede imaginar cómo un error de este tipo puede "explotar" cuando el programa se está ejecutando! Tenemos que evitar tales situaciones de alguna manera. Necesitamos restringir nuestro programa: al crear un nuevo Autoobjeto, queremos que los campos, como el modelo y la velocidad máxima, se especifiquen siempre. De lo contrario, queremos evitar la creación del objeto. Los constructores manejan esta tarea con facilidad. Obtuvieron su nombre por una razón. El constructor crea una especie de "esqueleto" de clase que debe coincidir con cada nuevo objeto. Para mayor comodidad, volvamos a la versión más simple de la clase Car con dos campos. Teniendo en cuenta nuestros requisitos, el constructor de la clase Car se verá así:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}

// And creating an object now looks like this:

public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 378);
}
Tenga en cuenta cómo se declara un constructor. Es similar a un método regular, pero no tiene un tipo de retorno. Además, el constructor especifica el nombre de la clase ( Car ) que comienza con una letra mayúscula. Además, el constructor se usa con una palabra clave que es nueva para ti: this . La palabra clave this es para indicar un objeto en particular. El código en el constructor.
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
se puede interpretar casi textualmente: "El modelo de este automóvil (el que estamos creando ahora) es el argumento del modelo pasado al constructor. La velocidad máxima para este automóvil (el que estamos creando) es el argumento de velocidad máxima pasado al constructor." Y eso es justo lo que sucede:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car("Bugatti Veyron", 378);
       System.out.println(bugatti.model);
       System.out.println(bugatti.maxSpeed);
   }

}
Salida de la consola: Bugatti Veyron 378 El constructor asignó correctamente los valores requeridos. ¡Puede que hayas notado que un constructor es muy similar a un método ordinario! Así es. Un constructor es realmente un método, pero con características específicas :) Al igual que con los métodos, pasamos argumentos a nuestro constructor. Y al igual que llamar a un método, llamar a un constructor no funcionará a menos que lo especifiques:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car(); // Error!
   }

}
Puede ver que el constructor logra lo que estábamos tratando de lograr. ¡Ahora no puedes crear un auto sin velocidad o modelo! La similitud entre constructores y métodos no termina aquí. Al igual que los métodos, los constructores pueden sobrecargarse. Imagina que tienes 2 gatos domésticos en casa. Obtuviste uno de ellos cuando eras un gatito. Pero el segundo lo sacaste de la calle cuando ya estaba grande, y no sabes exactamente cuántos años tiene. En este caso, queremos que nuestro programa sea capaz de crear dos tipos de gatos: los que tienen nombre y edad (para el primer gato), y los que solo tienen nombre (para el segundo gato). Para ello sobrecargaremos el constructor:
public class Cat {

   String name;
   int age;

   // For the first cat
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   // For the second cat
   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5);
       Cat streetCatNamedBob = new Cat("Bob");
   }

}
Además del constructor original con los parámetros "nombre" y "edad", agregamos uno más con solo un parámetro de nombre. Precisamente de la misma manera que sobrecargamos los métodos en lecciones anteriores. Ahora podemos crear ambos tipos de gatos :)
¿Por qué necesitamos constructores?  - 2
¿Recuerdas que al principio de la lección dijimos que ya has usado constructores sin darte cuenta? Queríamos decir lo que dijimos. El hecho es que cada clase en Java tiene lo que se llama un constructor por defecto. No toma ningún argumento, pero se invoca cada vez que crea un objeto de cualquier clase.
public class Cat {

   public static void main(String[] args) {

       Cat smudge = new Cat(); // The default constructor is invoked here
   }
}
A primera vista, es invisible. Creamos un objeto, ¿y qué? ¿Dónde está el constructor haciendo algo aquí? Para verlo, escribamos explícitamente un constructor vacío para la clase Cat . Mostraremos alguna frase en su interior. Si se muestra la frase, entonces se invocó al constructor.
public class Cat {

   public Cat() {
       System.out.println("A cat has been created!");
   }

   public static void main(String[] args) {

       Cat smudge = new Cat(); // The default constructor is invoked here
   }
}
Salida de la consola: ¡Se ha creado un gato! ¡Ahí está la confirmación! El constructor predeterminado siempre está presente de forma invisible en sus clases. Pero necesitas saber una cosa más al respecto. El constructor predeterminado se elimina de una clase una vez que crea un constructor con argumentos. De hecho, ya hemos visto pruebas de esto arriba. Estaba en este código:
public class Cat {

   String name;
   int age;

   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   public static void main(String[] args) {

       Cat smudge = new Cat(); //Error!
   }
}
No podíamos crear un gato sin nombre y edad, porque declaramos un constructor de gato con parámetros de cadena e int . Esto provocó que el constructor predeterminado desapareciera inmediatamente de la clase. Así que asegúrese de recordar que si necesita varios constructores en su clase, incluido un constructor sin argumentos, deberá declararlo por separado. Por ejemplo, supongamos que estamos creando un programa para una clínica veterinaria. Nuestra clínica quiere hacer buenas obras y ayudar a gatitos sin hogar cuyos nombres y edades se desconocen. Entonces nuestro código debería verse así:
public class Cat {

   String name;
   int age;

   // For cats with owners
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   // For street cats
   public Cat() {
   }

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5);
       Cat streetCat = new Cat();
   }
}
Ahora que hemos escrito un constructor predeterminado explícito, podemos crear ambos tipos de gatos :) Como con cualquier método, el orden de los argumentos pasados ​​a un constructor es muy importante. Intercambiemos los argumentos de nombre y edad en nuestro constructor.
public class Cat {

   String name;
   int age;

   public Cat(int age, String name) {
       this.name = name;
       this.age = age;
   }

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 10); // Error!
   }
}
¡Un error! El constructor estipula claramente que cuando se crea un objeto Cat , se le debe pasar un número y una cadena, en este orden. Entonces, nuestro código no funciona. Asegúrese de recordar esto y tenerlo en cuenta cuando declare sus propias clases:
public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}

public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
¡Estos son dos constructores totalmente diferentes! Si tuviéramos que expresar en una sola oración la respuesta a la pregunta "¿Por qué necesito un constructor?", podríamos decir, "Para asegurar que los objetos siempre tengan un estado válido". Cuando usa constructores, todas sus variables se inicializarán correctamente. Sus programas no tendrán autos con una velocidad de 0 o cualquier otro objeto "inválido". Su principal beneficio es para el programador. Si inicializa los campos manualmente (después de crear un objeto), existe un gran riesgo de que se le escape algo y presente un error. Pero esto no sucederá con un constructor: si no pasa todos los argumentos requeridos o pasa los tipos de argumentos incorrectos, el compilador registrará inmediatamente un error. También hay que decir por separado que no debe poner su programa' s lógica dentro de un constructor. Para eso están los métodos. Los métodos son donde debe definir toda la funcionalidad requerida. Veamos por qué agregar lógica a un constructor es una mala idea:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factory is called " + this.name);
   System.out.println("It was founded " + this.age + " years ago" );
   System.out.println("Since that time, it has produced " + this.carsCount +  " cars");
   System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
}

   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Ford", 115 , 50000000);
   }
}
Tenemos una clase CarFactory que describe la fábrica de automóviles. Dentro del constructor, inicializamos todos los campos e incluimos algo de lógica: mostramos información sobre la fábrica. Parece que no hay nada malo en esto. El programa funciona bien. Salida de la consola: Nuestra fábrica de autos se llama Ford Fue fundada hace 115 años Desde entonces, ha producido 50000000 autos En promedio, produce 434782 autos por año Pero en realidad hemos puesto una mina con retraso en el tiempo. Y este tipo de código puede conducir muy fácilmente a errores. Supongamos que ahora no estamos hablando de Ford, sino de una nueva fábrica llamada "Amigo Motors", que existe desde hace menos de un año y ha producido 1000 automóviles:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factor is called " + this.name);
   System.out.println("It was founded " + this.age + " years ago" );
   System.out.println("Since that time, it has produced " + this.carsCount +  " cars");
   System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
}


   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
   }
}
Salida de la consola: Nuestra fábrica de automóviles se llama Amigo Motors Exception en el subproceso "main" java.lang.ArithmeticException: / by zero Fue fundada hace 0 años Desde entonces, ha producido 1000 automóviles en CarFactory. (CarFactory.java:15) en CarFactory.main(CarFactory.java:23) El proceso terminó con el código de salida 1 ¡Auge! El programa termina con algún tipo de error incomprensible. ¿Puedes intentar adivinar la causa? El problema está en la lógica que ponemos en el constructor. Más específicamente, esta línea:
System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
Aquí está realizando un cálculo y dividiendo el número de automóviles producidos por la edad de la fábrica. Y dado que nuestra fábrica es nueva (es decir, tiene 0 años), dividimos por 0, lo que no podemos hacer en matemáticas. En consecuencia, el programa termina con un error.

¿Qué deberíamos haber hecho?

Ponga toda la lógica en un método separado. Llamémoslo printFactoryInfo() . Puede pasarle un objeto CarFactory como argumento. Puede poner toda la lógica allí y manejar simultáneamente errores potenciales (como el nuestro que involucra cero años). A cada uno lo suyo. Se necesitan constructores para establecer un estado de objeto válido. Tenemos métodos para la lógica de negocios. No mezcles uno con el otro. Para reforzar lo que aprendió, le sugerimos que vea una lección en video de nuestro Curso de Java
Comentarios
  • Populares
  • Nuevas
  • Antiguas
Debes iniciar sesión para dejar un comentario
Esta página aún no tiene comentarios