En PC Resumen hablaremos de la sobreescritura de métodos en el lenguaje Java. En una jerarquía de clases, cuando un método de una subclase tiene el mismo nombre y tipo que un método de su superclase, se dice que el método de la superclase sobrescribe el método de la superclase. Cuando se llama a un método sobrescrito desde una subclase, ésta siempre se refiere a la versión del método definida en la subclase. La versión del método definido por la superclase quedará oculta. Por ejemplo:
class A {
int i, j;
A(int a, int b) {
y = a;
j = b;
}
// Se imprimen i y j.
void show() {
System.out.println("i y j:" + y + "" + j);
}
}
class B extends A {
int k;
B (int a, int b, int c) {
super (a, b);
k = c;
}
// Se imprime k sobreescribiendo el método () en A.
void show() {
System.out.println("k:" + k);
}
}
class Override {
public static void main (String args[]) {
B subOb = new B (1, 2, 3);
subOb.show(); // llamada a show () en B.
}
}
La salida del programa es: k: 3
Cuando se llama a show en un objeto de tipo B, se utiliza la versión definida en B. Si se quiere acceder a la función sobrescrita, se utilizará el método super.
La sobreescritura de métodos sólo se da cuando los nombres y los tipos de dos métodos son idénticos, sino lo son, entonces los métodos simplemente están sobrecargados.
Selección dinámica de métodos
Aunque lo que hemos visto demuestra el mecanismo de sobreescritura de métodos, realmente no muestran todo su potencia.
La sobreescritura de métodos constituye la base de uno de los conceptos más potentes de Java: la selección dinámica de métodos, que es un mecanismo mediante el cual la llamada a un método sobrescrito se resuelve durante el tiempo de ejecución y no en el de compilación.
Cuando se llama a un método sobrescrito a través de una referencia a una superclase, Java determina qué versión de este método debe ejecutarse en función del tipo de objeto al que se hace referencia en el momento de hacerse la llamada.
Cuando se hace referencia a diferentes tipos de objetos, se llama a diferentes versiones del método sobrescrito. Dicho de otra forma, lo que determina la versión del método que será ejecutado es el tipo de objeto al que se hace referencia y no el tipo de variable de referencia. Por lo tanto, si una superclase contiene un método sobrescrito por una subclase, cuando se haga referencia a diferentes tipos de objetos mediante una variable de referencia de una superclase, se ejecutarán diferentes versiones del método.
Imaginemos este código:
class A {
void hola() {
System.out.println("Estoy a A");
}
}
class B extends A {
void hola() {
System.out.println("Estoy a B");
}
}
class C extends A {
void hola() {
System.out.println("Estoy a C");
}
}
class Saluda {
public static void main (String args []) {
A a = new A();
B b = new B();
C c = new C();
A r; // Obtención de una referencia a un objeto A
r = a;
r.hola();
r = b;
r.hola();
r = c;
r.hola();
}
}
La salida del programa sería:
- Estoy a A
- Estoy a B
- Estoy a C
Aplicación de la sobreescritura de métodos
Vemos ahora un ejemplo más práctico. El siguiente programa crea una superclase llamada Figura que almacena las dimensiones de varios objeto multimensionales. También define un método area que calcula el área del objeto. El programa deriva dos subclases de Figura, Rectángulo y Triángulo. Cada una de las subclases sobrescribe area para devolver el área del rectángulo y del triángulo.
class Figura {
double dim1;
double dim2;
Figura(double a, double b) {
dim1 = a;
dim2 = b;
}
double area() {
System.out.println("Area de Figura se indefinida.");
return 0;
}
}
class Rectángulo extends Figura {
Rectángulo(double a, double b) {
super(a, b);
}
// sobreescribe el área para un rectángulo.
double area() {
System.out.println("dentro Area de Rectángulo.");
return dim1 * dim2;
}
}
class Triángulo extends Figura {
Triángulo(double a, double b) {
super(a, b);
}
// sobreescribe el área para un triángulo
double area() {
System.out.println("dentro Area de Triángulo.");
return dim1 * dim2 / 2;
}
}
class Areas {
public static void main(String args[]) {
Figura f = new Figura(10, 10);
Rectángulo r = new Rectángulo(9, 5);
Triángulo t = new Triángulo(10, 8);
Figura figref;
figref = r;
System.out.println("Area is" + figref.area());
figref = t;
System.out.println("Area is" + figref.area());
figref = f;
System.out.println("Area is" + figref.area());
}
}
Gracias a los mecanismos duales de la herencia y del polimorfismo en tiempo de ejecución, es posible definir una interfaz consistente que se utilice para objetos de tipos diferentes aunque relacionados. En este caso, si se deriva un objeto de Figura puede obtenerse su área llamando al método area. La interfaz para esta operación es la misma, independientemente de cuál sea el tipo de figura que se esté utilizando.