Los constructores se definen de manera similar a un método, pero su identificador coincide con el nombre de la clases y no tienen tipo de retorno. Se le pueden aplicar igualmente modificadores de acceso.
Para crear objetos necesitamos un constructor. El identificador del constructor coincide con el nombre de la clase
El constructor por defecto
Vamos a seguir con nuestra claseCoche
para ver ejemplos. Éste es nuestro código actual: public class Coche {
String modelo;
String color;
int numeroDeRuedas;
@Override
public String toString() {
return modelo + " (" + color + "), " + numeroDeRuedas;
}
}
Como hemos dicho hace falta un constructor para instanciar un objeto pero no hemos declarado ninguno. A falta de la declaración de un constructor, Java nos proporcionará un constructor por defecto sin parámetros. Lo que hará será crear un objeto con los valores a los que hayamos inicializado sus variables de instancia. En este código no tenemos inicializada ninguna variable con lo que las variables modelo y color al ser del tipo String
tendrán el valor null
y numeroDeRuedas
tendrá valor cero.En Java instanciamos un objeto llamando al constructor mediante la palabra reservada
new
seguido del método constructor que queramos usar (en este caso el constructor por defecto Coche()
): Coche miCoche = new Coche();
System.out.println(miCoche);
La expresión con el new
devuelve un nuevo objeto del tipo Coche
. y al imprimirlo en consola nos sale:
Coche [modelo=null, color=null, numeroDeRuedas=0]
Asignación a un tipo de variable "compatible"
Al declarar de qué tipo es la variable se puede acceder a los miembros que forman parte de ese tipo de objetos a los que se tenga acceso. Cuando asignamos un valor a la variable vamos a apuntarla hacia el espacio de memoria que contiene al objeto creado, así podremos utilizar nuestro nuevo objeto del tipoCoche
.Recuerdo que es importante diferenciar el concepto de variable del concepto de objeto:
- Una variable puede usarse para referenciar a múltiples objetos compatibles, pero sólo a uno a la vez
- Las variables NO tienen una copia del objeto asignado ni ocupan espacio en la memoria como crear un nuevo objeto.
- Los objetos ocuparán el mismo espacio en memoria independientemente de las referencias que le apunten.
- Asignar de nuevo una variable no modifica el objeto que contenía antes, pero modificar un objeto hará que se observe ese cambio en todas las referencias al mismo objeto (ya lo vimos con el ejemplo de arrays).
- El tipo de la variable indica que el objeto deber "pertenecer" a ese tipo (en la línea jerárquica), aunque no quiere decir que sea una instancia de esa clase (podría ser de una clase que hereda o implementar esa interfaz entre otras). La línea jerárquica/herencia y la implementación de interfaces se verá en su sesión correspondiente.
- Si el objeto pertenece a una subclase/interfaz con más miembros, la variable nos dará acceso a los miembros del tipo declarado, independientemente del tipo real del objeto al que apunta.
- La variable puede apuntar a
null
lo que produciría un error en ejecución del tipoNullPointerException
si intentamos acceder a métodos de instancia. - Un objeto puede tener varias o ninguna referencia (en ese caso será destruido por el Recolector de Basura para liberar recursos porque ya no se puede volver a usar)
Una variable no es un objeto
Definición de constructores
Todos esperamos que un coche tenga cuatro ruedas. Vamos a declarar el constructor por defecto para que asigne este valor: public Coche () {
numeroDeRuedas = 4;
}
Este constructor ya es más correcto y ahora la salida del último código cambiará a:
Coche [modelo=null, color=null, numeroDeRuedas=4]
Siguiendo con el ejemplo, ahora que tenemos un objeto del tipo
Coche
, deberíamos tener métodos para manejar sus atributos. Por ejemplo vamos a crear métodos para leer el valor del color del coche y para establecerle uno nuevo. Éstos métodos los introdujimos en la sesión anterior sobre clases (getter y setter): public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
Hasta que yo no le asigne un valor al color del coche el valor será null
. Imaginemos que quisiéramos establecer un color determinado en el momento en que instanciamos nuestro coche. Con el constructor por defecto no podemos porque no se puede usar para asignar ningún color. Vamos a hacer una sobrecarga del constructor para tener uno que admita el color: public Coche (String color) {
numeroDeRuedas = 4;
setColor(color); // como ya tengo el setter lo utilizo
}
Observamos que se repite la inicialización numeroDeRuedas = 4;
si queremos que tome este valor al instanciarlo. Esto es un problema, si en un futuro los coches no necesitaran ruedas tendríamos que cambiar nuestros constructores (hay que huir de cambiar la misma línea en varios sitios).Para ello tenemos dos soluciones:
- Inicializar el valor en la declaración de variable. Para un número no hay problemas, pero para asignar otro valor que pueda dar error en su instanciación (un objeto) tenemos que protegernos de la posible excepción que rompería nuestro código. Este último problema lo solucionaremos en la sesión de inicializadores estáticos.
- Llamar al código que ya tenía.
public Coche (String color) {
this();
setColor(color);
}
Como vemos estamos llamando al constructor por defecto con this()
(aunque si tuviéramos más podríamos llamarlos igualmente simplemente proporcionando los parámetros que coincidieran con otro constructor) en nuestra primera línea, y así ya se asigna el valor 4 a las ruedas, y sólo queda añadir el código particular de este nuevo constructor. La única regla a esta forma es que la llamada al constructor debe ser la primera sentencia de todas.Te propongo como ejercicio que añadas otro constructor que también admita el modelo y cuando lo tengas acabado lo compruebes con la solución.
public Coche (String color, String modelo) {
this(color);
this.modelo = modelo;
}
No hay comentarios:
Publicar un comentario