[Las cosas que no interesan]

pageicon lunes jun 25, 2007

SCJP for JSE5 - 9

SOBREESCRITURA Y SOBRECARGA
1.  Sobreescritura de métodos.

La sobreescritura de métodos consiste en la habilidad de definir cierto tipo de comportamiento que es específico para una subclase. Por ejemplo:

class Humano{
    public void caminar(){
       //caminar como un ser humano
    }
    //otros métodos de humanos...
 }

class Pepe extends Humano{
    public void caminar(){
       //caminar de forma particular como Pepe
    }
}

En la implementación anterior la clase Humano define un grupo de métodos que aplicará a todas sus subclases. Pepe por ejemplo, hereda todos los métodos heredables de Humano, pero por alguna razón, define un comportamiento específico para caminar. Quizá por que Pepe sea cojo, tenga un pie más largo que otro o por que camina muy rápido, en fin, de esto se trata la sobreescritura de métodos: las subclases pueden aprovechar todos los métodos que heredan de sus padres y además personalizar el comportamiento que no se acomode a sus necesidades.

No se debe confundir la sobreescritura de métodos con la implementación. Cuando se heredan métodos abstractos de una clase (abstracta por supuesto), estos se implementan no se sobreescriben.

Las reglas para sobreescribir métodos son:

- La lista de argumentos debe ser exactamente igual a la del método sobreescrito. Si esta regla no se cumple, probablemente se obtiene un método sobrecargado.

- El tipo de retorno debe ser el mismo o un subtipo del tipo de retorno declarado en el método sobreescrito en la superclase. Lo de subtipo es nuevo en JSE5, se le llama "Covariant Returns" y se verá más adelante.

- El nivel de acceso no puede ser más restrictivo que el del método sobreescrito.

- Los métodos marcados como private, final o static no pueden ser sobreescritos.

- El método que sobreescribe no puede lanzar 'Checked Exceptions' nuevas o de nivel superior a las declaradas en el método sobreescrito.

- El método que sobreescribe no tiene que declarar excepciones que no vaya a lanzar, no importa las que hayan declaradas en el método original.

OJO: Si un método no puede ser heredado tampoco puede ser sobreescrito.

 
Invocar la versión de la superclase de un método sobreescrito

Sobreescribir un método no quiere decir que el método de la clase de nivel superior ya nunca más puede ser invocado con una instancia de la subclase; la palabra reservada super hace referencia a la instancia de la superclase (sí, al crear la instancia de una subclase, también se crea una de la superclase) y con esta palabra podemos invocar los métodos que han sido sobreescritos en la subclase. Por ejemplo:

class Humano{
    public void caminar(){
       //caminar como un ser humano
    }
 }

class Pepe extends Humano{
    public void caminar(){
       super.caminar(); //invoco la forma de caminar de la clase superior
       //implemento las particularidades del caminado de Pepe
    }
}

El uso de la palabra super para invocar métodos sobreescritos, está restringido para los métodos de instancia que sean accesibles por la subclase (los no private).

2. Sobrecarga de métodos

- La sobrecarga de métodos permite reutilizar el mismo nombre de un método en una clase pero con una lista de argumentos diferente y se puede cambiar el tipo de retorno. Los métodos se pueden sobrecargar en la misma clase o una subclase puede sobrecargar uno o varios métodos de la clase padre. Un ejemplo:

class Humano{
    public void caminar(int kilometros){
       //caminar los metros indicados
    }

    public void caminar(Recorrido rr){
       //seguir el recorrido pasado como parámetro
    }
 }

class Pepe{
    public void caminar(Direccion dir){
       //caminar hacia la dirección indicada
    }
}

Note que en el ejemplo anterior el método caminar() de la clase Pepe no sobreescribe ninguno de Humano sino que lo sobrecarga. Así una instancia de la clase Humano tiene acceso a dos formas del método caminar: una que recibe el número de kilómetros y otra que recibe el recorrido a hacer; mientras una instancia de la clase Pepe tiene acceso a tres formas del método caminar: dos heredadas de Humano y otra sobrecargada en Pepe que recibe una dirección.

Las reglas para sobrecargar métodos son:

- Los métodos sobrecargados tienen que cambiar la lista de argumentos.

- Un método sobrecargado puede cambiar el tipo de retorno.

- Un método sobrecargado puede cambiar el modificador de acceso.

- Un método sobrecargado puede declarar excepciones nuevas o de nivel superior.

Dado el método public Integer calcularPrecioProducto(int valorCompra, double ganancia) throws Exception, algunas versiones sobrecargadas de este método pueden ser:

protected Double calcularPrecioProducto(int valorCompra, double ganancia)
public Integer calcularPrecioProducto(int valorCompra)
public Integer calcularPrecioProducto(double ganancia) throws SQLException

- La decisión de la versión del método que se va a invocar está basada en la lista de argumentos y se hace en tiempo de compilación. Por ejemplo al compilar y ejecutar la clase EjemploSobrecarga:

class Humano{}
class Pepe extends Humano{}
class EjemploSobrecarga{
    void hacerAlgo(Humano hum){
        System.out.println("Procesando un Humano...");
    }

    void hacerAlgo(Pepe pe){
        System.out.println("Procesando Pepe...");
    }

    public static void main(String[] args) {
        Humano pepe = new Pepe();
        new EjemploSobrecarga().hacerAlgo(pepe);
    }
}

La salida será: "Procesando un Humano...".

Aunque la instancia que se pasa como parámetro al método 'hacerAlgo()' es de la clase Pepe, el compilador solo sabe que la variable es una referencia a la clase Humano y por tanto decide invocar la versión que recibe tiene esta clase como argumento. La salida sería diferente si la variable se creará así: 'Pepe pepe = new Pepe();', en este caso el compilador reconoce una referencia a Pepe e invoca el método correspondiente.

Algo que nunca me ha quedado muy claro es que sucede cuando se pasa null como argumento, no se cuál es el criterio para elegir el método. En el ejemplo anterior que sucedería con la línea: 'new EjemploSobrecarga().hacerAlgo(null);'

El Final

Es muy importante reconocer si un método se está sobreescribiendo, sobrecargando o ambos. Hay que reconocer cuando hay errores de compilación o cuando una mala sobreescritura termina en una sobrecarga; esto se debe complementar muy bien con los conceptos de polimorfismo para que dado cualquier código, con la complejidad que sea, podamos predecir la salida.

En SProgramando he publicado un listado de preguntas sobre el tema.

Comentarios:

Saludos,
Me podrías explicar porque la diferencia entre implementar y sobreescribir el método.

Enviado por roberto en junio 25, 2007 a las 02:55 PM COT #

Claro.
Un método se implementa cuando se crea código funcional (el que va entre las llaves, {}) para un método abstracto que se heredó de una clase abstracta o de alguna Interfaz implementada.
La sobreescritura consiste en crear un método igual (mismo nombre, lista de parámetros y valor de retorno) al que se hereda con el objetivo de darle algún comportamiento más específico para la subclase.

Espero hacerme entender, sino, pues haré un post más claro sobre el tema.

Saludos.

Enviado por azuluaga en junio 25, 2007 a las 06:10 PM COT #

pues gracias por tu respuesta, lamento que no comprendo pues, siempre he tenido la idea que sobreescribir es igual que implementar, pero en la interface le llamas implementar, no sobreescribir. El sobreescriibr tiene que ver con las clases hijas de extends ? nos podrias dar un ejemplo con codigo para estar mas claro.
Muchas gracias

Enviado por roberto en junio 26, 2007 a las 12:51 PM COT #

Implementamos un método: damos comportamiento específico a un metodo declarado como "abstract" en la clase padre o cuando implemtamos un metodo de alguna interfaz. Digamos que es la 1ra implementacion.

Sobrecargamos un método: cuando a un metodo del mismo nombre (ya implementado en la clase padre) le damos diferente comportamiento, ya sea porque retorna algo distinto o porque le pasamos diferentes parámetros.

Enviado por Daniel en julio 07, 2007 a las 05:05 PM COT #

Gracias Daniel, espero que continúes con estos artículos, nos ilumina mucho.

Enviado por Roberto en julio 09, 2007 a las 02:13 PM COT #

Que onda, tienes error en las definiciones

public Integer calcularPrecioProducto(int valorCompra, double ganancia) throws Exception

protected Double calcularPrecioProducto(int valorCompra, double ganancia)

la lista de argumentos es la misma, en este caso no es una buena sobrecarga....

Saludos

Enviado por Arturo M. A. en julio 26, 2007 a las 01:13 AM COT #

que pasa con el blog?

Enviado por Pedro en septiembre 17, 2007 a las 09:38 PM COT #

Enviar un comentario:
Los comentarios han sido deshabilitados.