Programación 3
Universidad de Alicante, 2018–2019
Ejercicios sobre enlace dinámico y sobrescritura
(Pulsa aquí para la versión para imprimir)
Ejemplo visto en clase (UD 5)
En la figura puedes ver un ejemplo de la organización en memoria de los objetos y clases de un programa en ejecución. Las variables locales y argumentos de un método se almacenan en la pila (stack), mientras que los objetos se almacenan en el heap o, como se conoce en la jerga de C++, la memoria dinámica. En el área de métodos (method area) se almacenan las implementaciones de los métodos (sus instrucciones).
Cuando creamos un objeto (con new
), este se crea en el heap y su dirección se guarda en una referencia en el stack (la variable a
del ejemplo).
Date cuenta como, en tiempo de ejecución, la máquina virtual dispone de la información necesaria para enlazar las llamadas a.walk()
y a.die()
en tiempo de ejecución.

Prueba lo siguiente:
- Reproduce mentalmente los pasos que sigue la máquina virtual para encontrar el código del método que tiene que enlazar a las llamadas
a.walk()
ya.die()
- Indica qué ocurre si escribimos la instrucción
a.meow()
- Indica cómo harías ladrar al perro.
- Añade un segundo perro al código y modifica el diagrama para reflejar esa situación.
- ¿Qué ocurre si invocamos a
Animal.getCount()
? - Añade un gato al código, guardando la dirección del nuevo objeto en una referencia de tipo Animal. Modifica el diagrama para reflejar la situación. Ejecuta el método
walk()
sobre él y reproduce los pasos del enlace dinámico como hiciste antes. ¡Deberías llegar al código de Cat.walk()!
Copia profunda y copia superficial
El siguiente código define las clases Circulo
y Coord
.
class Circle {
private double r;
private Coord c;
public static final PI = 3.141592;
public Circle() {
r = 10.0;
c = new Coord(0,0);
}
public Circle(Circle c) {
// TODO: implementar como
// 1. copia superficial
// 2. copia profunda
}
}
class Coord {
private double x;
private double y;
public Coord(double a, double b) {
x = a;
y = b;
}
public Coord(Coord c) {
x = c.x;
y = c.y;
}
//... getters, etc...
}
//... código cliente:
Circle c1 = new Circle();
Circle c2 = new Circle(c1);
Haz lo siguiente:
Implementa el constructor de copia de Circulo como copia superficial y dibuja un diagrama similar al del ejercicio anterior que refleje la situación en memoria en tiempo de ejecución tras ejecutar el código cliente.
Implementa el constructor de copia de Circulo como copia profunda y refleja en el diagrama los cambios que se producirían.
Covarianza (sobrescritura, UD6)
El siguiente código crea dos jerarquías de herencia: la superclase A y su subclase B, y la superclase Super y su subclase Sub. Estas dos últimas contienen un subobjeto de clase A y B, respectivamente. Fíjate como el método getA() devuelve un B en la clase Sub. A este cambio en el tipo devuelto por un método sobrescrito se le llama covarianza y a los tipos relacionados (A y B), tipos covariantes. Siempre deben ser tipos relacionados mediante herencia.
class A {
public A() {...}
}
class B extends A {
public B() {...}
}
class Super {
private A objA;
public Super(A obj) {
objA = obj;
}
public A getA() {
return objA;
}
}
class Sub extends Super {
private B objB;
public Sub(B obj) {
objB = obj;
}
@Override
public B getA() {
return objB;
}
}
// ... código cliente...
A a = new A();
B b = new B();
Super sp = new Super(a);
Sub sb = new Sub(b);
// 1
a = sp.getA();
a = sb.getA();
Haz lo siguiente:
Dibuja, como antes, el diagrama de la disposición en memoria de los objetos y sus objetos Class tras la ejecución del código cliente hasta el comentario 1.
Explica qué ocurre con las dos llamadas a getA(), basándote en el diagrama.