Buenas tardes, compañeros.
Aquí dejo una posible solución del ejercicio CU00690B del curso Java desde cero.
EJERCICIO
Responde a las siguientes preguntas:
Supongamos la superclase Vehiculo que representa a cualquier tipo de vehículo y la subclase Taxi que representa a un tipo de vehículo concreto.
c) Escribe el código de una clase Vehiculo con los atributos matricula (String) y numeroDeRuedas (int), constructor, métodos getters y setters y método toString() para mostrar la información de un vehículo.
Código de la clase "Vehiculo":
public class Vehiculo{
private String matricula;
private int numeroDeRuedas;
public Vehiculo(String matricula, int numeroDeRuedas){
this.matricula = matricula;
this.numeroDeRuedas = numeroDeRuedas;
}
public void setMatricula(String matricula){
this.matricula = matricula;
}
public void setNumeroDeRuedas(int numeroDeRuedas){
this.numeroDeRuedas = numeroDeRuedas;
}
public String getMatricula(){return matricula;}
public int getNumeroDeRuedas(){return numeroDeRuedas;}
public String toString(){
return "El vehículo dispone de: "+getNumeroDeRuedas()+" ruedas y tiene una matrícula: "+getMatricula();
}
}
d) Escribe el código de una clase Taxi que herede de vehículo y que además de los atributos de Vehiculo lleve un atributo adicional nombreDelConductor (String) y numeroDePlazas (int), constructor, métodos getters y setters y método toString() para mostrar la información de un Taxi.
Código de la clase "Taxi":
public class Taxi extends Vehiculo{
private String nombreDelConductor;
private int numeroDePlazas;
public Taxi(String matricula, int numeroDeRuedas, String nombreDelConductor, int numeroDePlazas){
super(matricula, numeroDeRuedas);
this.nombreDelConductor = nombreDelConductor;
this.numeroDePlazas = numeroDePlazas;
}
public void setNombreDelConductor(String nombreDelConductor){
this.nombreDelConductor = nombreDelConductor;
}
public void setNumeroDePlazas(int numeroDePlzas){
this.numeroDePlazas = numeroDePlazas;
}
public String getNombreDelConductor(){return nombreDelConductor;}
public int getNumeroDePlazas(){return numeroDePlazas;}
public String toString(){
return "El vehículo dispone de: "+getNumeroDeRuedas()+" ruedas, tiene una matrícula: "+getMatricula()+
" y el conductor se llama: "+getNombreDelConductor()+". El taxi dispone de: "+getNumeroDePlazas()+" plazas.";
}
}
a) ¿Un objeto de tipo estático declarado Taxi puede contener a un objeto Vehiculo en tiempo de ejecución?
Este código no funcina:
public class TestEnmascaramiento01{
public static void main(String []Args){
Vehiculo vehiculo1 = new Vehiculo("8379 DRT",4);
Taxi taxi1 = new Taxi("2039 DWQ",9,"Jose",20);
taxi1 = vehiculo1;
}
}
Y este, en cambio, sí:
public class TestEnmascaramiento01{
public static void main(String []Args){
Vehiculo vehiculo1 = new Vehiculo("8379 DRT",4);
Taxi taxi1 = new Taxi("2039 DWQ",9,"Jose",20);
taxi1 = (Taxi) vehiculo1;
}
}
La explicación de esto la encontramos en la entrega CU00689B y precisamente aquí:
2. Conversión hacia abajo
Se trataría de poner lo que está arriba abajo, por ejemplo poner un Profesor como ProfesorInterino. Esto no siempre es posible. El supertipo admite cualquier forma (es polimórfico) de los subtipos: si el supertipo almacena el subtipo al que queremos realizar la conversión, será posible usando lo que se denomina “Enmascaramiento de tipos” o “Hacer Casting” (cast significa “moldear”). Si el supertipo no almacena el subtipo al que queremos convertirlo, la operación no es posible y saltará un error. Ejemplo:
Profesor p1; //p1 es tipo Profesor. Admite ser Profesor, ProfesorTitular o ProfesorInterino.
ProfesorInterino p44 = new ProfesorInterino(); //p44 es ProfesorInterino.
p1 = p44; // Conversión hacia arriba: sin problema. Ahora p1 que es tipo profesor, almacena un profesor interino
p44 = p1 // ERROR en la conversión hacia abajo. El compilador no llega tan lejos como para saber si p1 almacena un profesor interino u otro tipo de profesor y ante la incertidumbre salta un error. La forma de forzar al compilador a que “comprenda” que p1 está almacenando un profesor interino y por tanto puede asignarse a una variable que apunta a un profesor interino se llama “hacer casting”. Escribiríamos lo siguiente: p44 = (ProfesorInterino) p1;
b) ¿Un objeto de tipo estático declarado Vehiculo puede contener a un objeto Taxi en tiempo de ejecución?
public class TestEnmascaramiento02{
public static void main(String []Args){
Vehiculo vehiculo1 = new Vehiculo("8379 DRT",4);
Taxi taxi1 = new Taxi("2039 DWQ",9,"Jose",20);
vehiculo1 = taxi1;
}
}
Vemos que el código compila.
1. Conversión hacia arriba
Se trataría por ejemplo de poner lo que está a un nivel inferior en un nivel superior, por ejemplo poner un profesor interino como profesor. Posible código: profesor43 = profesorinterino67;
Ponemos lo que está abajo (el profesor interino) arriba (como profesor). Esto es posible, pero dado que lo que está abajo generalmente contiene más campos y métodos que lo que está arriba, perderemos parte de la información. Sobre el objeto profesor43 ya no podremos invocar los métodos propios de los profesores interinos.
e) Escribe el código de una clase test con el método main que cree un objeto cuyo tipo es Vehiculo, instanciado como Taxi. Establece valores para sus atributos y usa el método toString(). ¿Qué método toString() resulta de aplicación, el propio de la clase Vehiculo o el propio de la clase Taxi? ¿Por qué?
Código de la clase "Test":
public class Test{
public static void main(String []Args){
Vehiculo vehiculo = new Taxi("4389 BTN",4,"Juan",8);
System.out.println(vehiculo.toString());
}
}
La respuesta a la pregunta "e" la encontramos al final de la entrega CU00690B y precisamente aquí:
el compilador se basa en el tipo estático para su trabajo, podríamos pensar que si invocamos a un objeto de tipo estático “superclase” y tipo dinámico “subclase” con un método sobreescrito, el método que se utilice sería el propio de la superclase. Pero sin embargo, esto no es así: el control de tipos del compilador se basa en los tipos estáticos pero en tiempo de ejecución los métodos que se ejecutan dan preferencia al tipo dinámico. Es decir, en tiempo de ejecución Java está constantemente “buscando” (“ligando” o “despachando”) el método que corresponda en función del tipo dinámico al que apunta una variable.
Ampliación de la información:
Si el método invocado no se encuentra definido en el tipo dinámico de que se trate, Java busca el método en la superclase para tratar de ejecutarlo. Si no lo encuentra en la superclase, continúa subiendo niveles de clase hasta alcanzar a la clase Object. Si en la clase Object tampoco se encontrara un método con ese nombre, el programa no llegaría a compilar. Decimos que los métodos en Java son polimórficos porque una misma llamada en un programa puede dar lugar a la ejecución de distintos métodos según el tipo dinámico de una variable.
Gracias.