RSS

Archivo de la categoría: Tema 2

Orientación a Objetos (VIII) – Acoplamiento y Cohesión

Uno de los objetivos más importantes del diseño orientado a objetos es conseguir una alta cohesión entre clases y un bajo acoplamiento.

¿Qué es la cohesión?
La medida que indica si una clase tiene una función bien definida dentro del sistema. El objetivo es enfocar de la forma más precisa posible el propósito de la clase. Cuanto más enfoquemos el propósito de la clase, mayor será su cohesión.

Una prueba fácil de cohesión consiste en examinar una clase y decidir si todo su contenido está directamente relacionado con el nombre de la clase y descrito por el mismo.

Una alta cohesión hace más fácil:

  • Entender qué hace una clase o método
  • Usar nombres descriptivos
  • Reutilizar clases o métodos

 

¿Qué es el acoplamiento?
El acoplamiento entre clases es una medida de la interconexión o dependencia entre esas clases.

El acoplamiento fuerte significa que las clases relacionadas necesitan saber detalles internos unas de otras, los cambios se propagan por el sistema y el sistema es posiblemente más difícil de entender.

Por ello deberemos siempre intentar que nuestras clases tengan un acoplamiento bajo. Cuantas menos cosas conozca la clase A sobre la clase B, menor será su acoplamiento.

Lo ideal es conseguir que la clase A sólo conozca de la clase B lo necesario para que pueda hacer uso de los métodos de la clase B, pero no conozca nada acerca de cómo estos métodos o sus atributos están implementados.

Los atributos de una clase deberán ser privados y la única forma de acceder a ellos debe ser a través de los métodos getter y setter.

Un bajo acoplamiento permite:

  • Entender una clase sin leer otras
  • Cambiar una clase sin afectar a otras
  • Mejora la mantenibilidad del código

Para saber más:
Msdn Magazine – Cohesión y acoplamiento
Cohesion and Coupling Metrics(1)
Cohesion and Coupling Metrics(2)

 
4 comentarios

Publicado por en 16 junio, 2011 en Estudio, Tema 2

 

Etiquetas: , , , , , ,

Orientación a Objetos (VII) – Miembros estáticos

Static

La palabra clave static declara miembros (atributos, métodos y clases anidadas) que están asociados a la clase en lugar de a una instancia de la clase.

Utilidad

A veces puede ser útil tener una variable compartida por todos los objetos de la clase (algo parecido a una variable global). En este caso marcaríamos esta variable como static.

Si una variable static no se marca como private, es posible acceder a ella desde fuera de la clase. Para hacerlo, no se necesita ningún objeto, se hace referencia a ella mediante el nombre de la clase.

Por ejemplo, un caso común es un contador de los objetos instanciados de una clase:

public class Leccion{
    public static int contador; //variable estática, asociada a la clase
    private int idLeccion;      //atributos, variables no estáticas
    private String desLeccion; //asociadas a los objetos

    public Leccion()
    {
        contador = contador + 1;  //podemos usar el contador para el id de cada objeto Leccion.
        idLeccion = contador;
        desLeccion = "Sin nombre";
    }
}

 

public class MainLeccion{
    public static void main (String[] args){
        System.out.println("Contador: " + Leccion.contador); //Muestra 0
        Leccion l1 = new Leccion();
        Leccion l2 = new Leccion();
        System.out.println("Contador: " + Leccion.contador); //Muestra 2
    }

}

O cuando necesitemos un método cuyo código no dependa de los atributos de un objeto de la clase. En este caso marcaríamos este método como static.

Por ejemplo, podríamos hacer la variable estática anterior privada y obtener el número de objetos Leccion creados mediante un método getContador().

public class Leccion{
    private static int contador; //variable estática, asociada a la clase
    public static int getContador(){ //método estático
         return contador;
    }

    private int idLeccion;     //atributos, variables no estáticas
    private String desLeccion; //asociadas a los objetos

    public Leccion()
    {
        contador = contador + 1;  //podemos usar el contador para el id de cada objeto Leccion.
        idLeccion = contador;
        desLeccion = "Sin nombre";
    }
}

En la clase principal ahora llamaríamos al método estático:

public class MainLeccion{
    public static void main (String[] args){
        System.out.println("Contador: " + Leccion.getContador()); //Muestra 0
        Leccion l1 = new Leccion();
        Leccion l2 = new Leccion();
        System.out.println("Contador: " + Leccion.getContador()); //Muestra 2
    }


Accediendo a Miembros Estáticos

Anteriormente hemos visto que para acceder a las variables y métodos estáticos hemos utilizado el nombre de la clase y el operador punto. Es lo más lógico dado que las variables y métodos estáticos pertenecen a la clase, pero Java permite también acceder a los miembros estáticos utilizando un objeto.

Java lo permite pero el compilador realmente lo sustituirá por el nombre de la clase.

En el ejemplo anterior podíamos haber escrito:

 System.out.println("Contador: " + l2.getContador());

Esto último solo lo podremos hacer si existe una instancia de la clase.

Redefinición en Métodos Estáticos

Los métodos estáticos no se pueden sobrescribir (override), sin embargo si es posible tener el mismo método en una subclase. Esto se denomina redefinición o ocultación.

Si se llama al método con una referencia de objeto, el método llamado será el correspondiente a la clase para la que se haya declarado la variable.

¿Qué diferencia existe entre la sobrescritura y la redefinición?

La sobrescritura (override) está íntimamente ligada al polimorfismo. Como los métodos estáticos están asociados a la clase en lugar de a los objetos, el polimorfismo en tiempo de ejecución no es posible y por lo tanto no es sobrescritura sino redefinición.

Veamos la diferencia. Creamos una clase Persona y una subclase Cliente. Creamos un método no estático, mostrar() y un método estático, mostrarStatic() en cada una de las clases.

public class Persona{
    private String nif;
    private String nombre;
    private int edad;

    public Persona(String nif, String nombre, int edad){
        this.nif = nif;
        this.nombre = nombre;
        this.edad = edad;
    }
    ... //getters, setters

    public void mostrar(){
        System.out.println("Persona " + nif + ":  \n Nombre: " + nombre + "\n Edad: " + edad);
    }
    public static void mostrarStatic(){
        System.out.println("Clase Persona");
    }
}

 

public class Cliente extends Persona{
    private int id;
    private int antiguedad;
    private int descuento;

    public Cliente(String nif, String nombre, int edad, int id, int antiguedad, int descuento) {
        super(nif, nombre, edad);
        this.id = id;
        this.antiguedad = antiguedad;
        this.descuento = descuento;
    }
    ... // getters, setters

    public void mostrar(){
        System.out.println("Cliente" + id + ":  \n Nombre: " + getNombre()
         + "\n Antigüedad: " + antiguedad + "\n Descuento: " + descuento);
    }
    public static void mostrarStatic(){
        System.out.println("Clase Cliente");
    }
}

Con esta definición de clases si creamos los siguientes objetos tendríamos los siguientes resultados:

  public class MainSobrescritura{
      public static void main(String[] args){
          Persona per = new Persona("18520147L", "Maria Victoria Rodriguez", 18);
          Cliente cli = new Cliente("20415789M", "Secundino Jimenez", 25, 504, 5, 1);
          Persona per2 = new Cliente("41605788R", "Luis Martin", 30, 905, 3, 2);

          per.mostrar(); //llama a mostrar() de Persona
          cli.mostrar(); //llama a mostrar() de Cliente
          per2.mostrar();//llama a mostrar() de Cliente
      }
  }

Ahora veamos lo que ocurre con un método estático:

public class MainRedefinicion{
    public static void main(String[] args){
       Persona per = new Persona("18520147L", "Maria Victoria Rodriguez", 18);
       Cliente cli = new Cliente("20415789M", "Secundino Jimenez", 25, 504, 5, 1);
       Persona per2 = new Cliente("41605788R", "Luis Martin", 30, 905, 3, 2);

        per.mostrarStatic(); //llama a mostrarStatic de Persona
        cli.mostrarStatic(); //llama a mostrarStatic de Cliente
        per2.mostrarStatic();//llama a mostrarStatic de Persona y no de Cliente como arriba.
    }
}

Vemos que en los métodos estáticos el método llamado será el de la clase declarada en tiempo de compilación y no en tiempo de ejecución como pasa con los métodos no estáticos sobrescritos.

Acceso en un método estático

Un método estático no puede acceder a ninguna variable salvo a las variables locales, los atributos static y sus parámetros. Cualquier intento de acceder a atributos que no sean estáticos directamente (sin crear un objeto) provoca un error de compilación.

Nota

El método main es también un método estático. Podemos comprobar que no creamos ningún objeto para ejecutarlo.Si necesita datos miembro se deben crear objetos dentro de main.

 
1 comentario

Publicado por en 10 junio, 2011 en Estudio, Tema 2

 

Etiquetas: , , , , ,

Orientación a Objetos (VI) – Constructores

Constructores

Los constructores son métodos especiales que se utilizan para inicializar un objeto de una clase.
Los constructores son la única forma de crear un objeto en Java.

El nombre del constructor debe ser idéntico al de la clase y no tendrá tipo de retorno. Los modificadores de acceso válidos para los constructores son: public, protected y private.

Una clase puede tener cualquier número de constructores. Todos tendrán el mismo nombre y al igual que otros métodos sobrecargados, los constructores se diferenciaran unos de otros en el número y en el tipo de sus parámetros.

La sintaxis será:
[modificador acceso] nombreClase ([listaParámetros]) {…}

Encadenamiento de Constructores

Para llamar al constructor utilizaremos la palabra clave new. Además, con esta palabra clave, no sólo estamos invocando al constructor de la clase de nuestro objeto, sino también a los constructores de las superclases.

Por ejemplo, imaginemos que tenemos la siguiente clase:

 public class Solicitante extends Persona extends Object {
 ...
 }
 

Al crear un objeto con new,

...
 Solicitante solAyuda1 = new Solicitante();
...

se llamará al constructor de Solicitante, que llamará al constructor de Persona, que llamará al constructor de Object. Después, terminará el constructor de Object, terminará el constructor de Persona inicializando los atributos que le correspondan, y terminará el constructor de Solicitante inicializando los atributos que le correspondan.

El Constructor es Obligatorio

Todas las clases deben tener un constructor, incluso las clases abstractas.

Si no escribimos un constructor para una clase el compilador añade uno por defecto.
En este caso, el constructor no tendrá parámetros, su cuerpo estará vacío y tendrá el mismo modificador de acceso que la clase.

Por ejemplo, para esta clase que no hemos escrito constructor:

public class Persona{
    private String id;
    private String nombre;
    private String movil;
}

El compilador insertaría:

public class Persona{
    private String id;
    private String nombre;
    private String movil;
    public Persona(){}
}

En el momento en que escribamos un constructor (con o sin parámetros) el constructor por defecto se perderá.

A Recordar

  • Cada constructor debe tener en su primera línea una llamada a otro constructor sobrecargado, (this) o una llamada al constructor de la superclase (super).
  • En el caso de que no escribamos la llamada a super, el compilador insertará automáticamente una llamada a super sin parámetros, super().
  • Sólo es posible llamar a a un constructor desde otro constructor.
  • Las interfaces no pueden tener constructores, ya que no son clases.
  • La llamada al constructor de la superclase mediante super puede ser con o sin parámetros.
  • Si el constructor de la superclase necesita parámetros y en el constructor de la subclase hacemos una llamada a super sin parámetros fallará.
  • Las clases abstractas también tienen constructores. Estos constructores serán llamados cuando se ejecute el constructor de las subclases que la concretan.

Ver También: Sobrecarga de Constructores

 
1 comentario

Publicado por en 8 junio, 2011 en Estudio, Tema 2

 

Etiquetas: , , , ,

Orientación a objetos (V) – Implementación de interfaces y tipos de retorno.

Implementando Interfaces

Decíamos con anterioridad que las interfaces son una especie de “contrato”, una declaración del conjunto de métodos que podemos utilizar si una clase implementa dicha interfaz.

Por tanto, una clase al implementar una interfaz debe definir cada uno de los métodos de ésta. De esta forma, cualquier persona que instancie un objeto de la clase implementadora tiene la seguridad de que puede llamar a los métodos de la interfaz.

Cuando Java encuentra una clase que dice implementar una interfaz la comprueba. Si falta algún método por definir nos dará error de compilación. Basta que tengamos la definición de los métodos aunque no tengamos ningún código para ellos.

Por ejemplo, esta clase no daría ningún error:

public interface Cultivable{
  public void plantar();
  public void regar();
  public int cosechar();
}

public class cultivoFresas implements Cultivable {
....
  public void plantar() {}
  public void regar(){}
  public int cosechar(){}
...
}

También es posible que nuestra clase implementadora sea abstracta. En este caso la clase puede elegir entre implementar algunos métodos, todos o ninguno. Y sería alguna clase concreta de ésta la que defina los restantes.

Por ejemplo:

 public abstract class Cultivos implements Cultivable {
   public void plantar() { ... } //Ejemplo de clase abstracta
   public void regar() { ... } // que define algunos métodos
 }

public class Maiz extends Cultivos {
   public void int cosechar() { ... } //Y el que falta en la concreta.
}

Las clases que implementan una interfaz deben seguir las misas normas que las clases que extienden una clase abstracta:

  • Deben definir todos los métodos de la interfaz.
  • Seguir todas las normas para las sobrescrituras (overrides).
  • Declarar sólo las excepciones comprobadas definidas en la interfaz o subclases de éstas.
  • Mantener la signatura de los métodos y el tipo devuelto (o un subtipo de éste).

Hay que recordar también que:

  • Una clase puede extender a sólo una clase.
  • Una clase puede implementar varias interfaces.
  • Una interfaz puede extender una o varias interfaces, pero nunca puede implementar.


Tipos de Retorno Legales

Vamos a hacer un pequeño resumen de qué podemos declarar como tipos de retorno:

  • Métodos sobrecargados: un método sobrecargado es un método totalmente diferente y nuevo.
    Si tenemos un método en una clase y lo sobrecargamos en una subclase no tiene que regirse por las normas de los métodos sobrescritos. Podemos declarar cualquier tipo de retorno.Hay que recordar que la sobrecarga no sólo se puede basar en un tipo diferente de retorno, debía diferenciarse del método original por el número o tipo de parámetros.
  • Métodos sobrescritos: un método sobrescrito consistía en dar una implementación distinta a un  método en una subclase. El método debía coincidir exactamente. A partir de Java 5, se permite también que el tipo de retorno en el método sobrescrito sea una subclase del tipo de retorno del método en la superclase.

Para devolver correctamente un valor para que se ajuste al tipo de retorno, hay que tener en cuenta:

  • Sólo podemos devolver null si el tipo de retorno es una variable de referencia (objeto y no tipo primitivo).
  • Si el tipo de retorno es un tipo primitivo cualquier valor que sea del tipo o que se pueda convertir
    implícitamente o explícitamente (usando un cast).
  • Nunca retornar un valor si el tipo de retorno es void.
  • Si el tipo de retorno es una variable de referencia (objeto y no tipo primitivo) podemos devolver un objeto de una subclase, ya que se hace el cast implícitamente.
  • Si el tipo de retorno es una interfaz podemos devolver cualquier objeto de una clase que implemente dicha interfaz.
 
1 comentario

Publicado por en 26 mayo, 2011 en Estudio, Tema 2

 

Etiquetas: , , , ,

Orientación a objetos (IV) – Conversiones de tipos

Operador instanceof

Como hemos visto, es posible utilizar objetos mediante referencias a sus superclases. Por lo tanto, a veces es necesario saber con qué objetos reales estamos tratando. Para ello tenemos el operador instanceof.

Por ejemplo, si tenemos la siguiente jerarquía de clases, utilizaríamos el operador instanceof para realizar unas acciones u otras dependiendo del objeto que realmente tengamos:

public class Contrato{...}

 

 public class Deposito extends Contrato{...}

 

 public class Prestamo extends Contrato{...}

 

public void finalizarContrato(Contrato c){
    if (c instanceof Deposito){
        //Devolver intereses
        //Poner indicador de contrato vivo a falso
    }
    else if (c instanceof Prestamo){
        //Poner importePendiente a 0
        //Poner indicador de contrato vivo a falso
    }
}

¿Como podemos realizar estas acciones si para nuestro compilador c es de tipo Contrato y no podríamos acceder a métodos de Deposito y de Prestamo? Haciendo una conversión de tipos.

Conversión de tipos

En los casos como el anterior, en el que ya hemos comprobado mediante el operador instanceof que el objeto es una subclase concreta, es posible acceder a toda la funcionalidad del objeto convirtiendo la referencia.

Para el ejemplo anterior sería:

public void finalizarContrato(Contrato c){
    if (c instanceof Deposito){
        Deposito d = (Deposito) c;
        //Devolver intereses
        d.devolverIntereses();
        //Poner indicador de contrato vivo a falso
        d.setIndContratoVivo(false);
    }
    else if (c instanceof Prestamo){
        Prestamo p = (Prestamo)c;
        //Poner importePendiente a 0
        p.setImportePendiente(0);
        //Poner indicador de contrato vivo a falso
        p.setIndContratoVivo(false);
    }
}

Si no realizamos la conversión, cualquier llamada a un método provocará error al no localizar tal método en la superclase.

Debemos tener en cuenta:

  • Las conversiones en dirección ascendente (de un objeto de una subclase a una referencia a una superclase o interfaz) siempre están permitidas, y de hecho, no precisan el operador de conversión. Se pueden hacer mediante una simple asignación.
    Prestamo pr = new Prestamo();
    Contrato c = pr;  //"Padre" = "Hijo" siempre posible y no necesario cast.
    

    Si Prestamo implementa Firmable con el método firmar, también es legal la asignación directa:

    Prestamo pr = new Prestamo();
    Firmable f = prestamo;
    f.firmar(); //podremos llamar a los métodos de la interfaz,
    //ya que están implementados en Prestamo.
    

    Y más aún, si tenemos la clase PrestamoHipotecario que hereda de Prestamo, que implementa a Firmable, también podemos hacer la asignación directa. Ya que PrestamoHipotecario hereda también la función Firmar por Prestamo implementar a Firmable.

    PrestamoHipotecario hip = new PrestamoHipotecario();
    Firmable f = hip;
    f.firmar();
    
  • En las conversiones de dirección descendente (de un objeto de una superclase a una referencia a una subclase), el compilador debe poder considerar que la conversión es posible, para esto, la clase destino de la conversión tiene que ser una subclase del tipo de referencia actual.
    Contrato c = new Contrato();
    Prestamo p = (Prestamo) c; // "Hijo" = "Padre" necesita cast.
    //El compilador no dará fallo pues parece posible la conversión.
    //Prestamo es una subclase de Contrato.
    
    Empleado e = new Empleado();
    Prestamo p = (Prestamo) e; //En este caso el compilador considera
    //que es imposible la conversión. Ni siquiera está en la jerarquía.
    //Provocará un error 'Inconvertible types'
    
  • Si el compilador permite la conversión, todavía no podemos cantar victoria, el tipo de objeto se comprueba durante el tiempo de ejecución. Si el objeto que se va a convertir no es realmente del tipo al que se hace la conversión, se producirá un error en tiempo de ejecución. Será una excepción ClassCastException.
    Contrato c1 = new Contrato();
    Contrato c2 = new Prestamo();
    
    Prestamo p1 = (Prestamo) c1; //Compila pero genera ClassCastException al ejecutar.
    Prestamo p2 = (Prestamo) c2; //Compila y se ejecuta correctamente.
    
 
1 comentario

Publicado por en 12 mayo, 2011 en Estudio, Tema 2

 

Etiquetas: , , , ,

Orientación a objetos (III) – Sobrecarga Constructores

Sobrecarga de constructores

Una clase puede definir varios constructores para permitir inicializar un objeto con más o menos atributos.
Podríamos tener un constructor sin argumentos para crear un objeto con unos atributos por defecto y varios constructores con argumentos dependiendo de a qué atributos queramos dar valor.

Por ejemplo, para la clase Tecnico:

 public class Tecnico{
 public static final double SALARIO_BASE = 600;
 private String nombre;
 private int departamento;
 private float sueldo;

public Tecnico(){   //Constructor sin parámetros.
 nombre = "";
 departamento = 0;
 sueldo = SALARIO_BASE;
 }

public Tecnico(String nombre, int departamento, float sueldo){   //Constructor con todos los parámetros.
 this.nombre = nombre;
 this.departamento = departamento;
 this.sueldo = sueldo;
 }

public Tecnico(String nombre, float sueldo){   //Constructor con tres parámetros, que utiliza this().
 this(nombre, 0, sueldo);
 }

}

El primer constructor sin parámetros inicializa todos los atributos con valores por defecto para nombre, departamento y sueldo.

El segundo constructor tiene parámetros para cada atributo de Tecnico.
El tercer constructor tiene dos parámetros, nombre y sueldo. La referencia this se utiliza como forma de envío de llamada a otro constructor (siempre dentro de la misma clase), en este caso el segundo constructor.

Es decir en esta llamada:

Tecnico t3 = new Tecnico (“Jose Luis”, 1300.50f);
Estaremos invocando al constructor con tres parámetros así:  Tecnico(“Jose Luis”, 0, 1300.50f)

Los constructores no se heredan

Aunque las subclases heredan todos los métodos y variables de sus superclases, no heredan sus constructores.

Las clases sólo pueden obtener un constructor de dos formas: debe escribirlo el programador o, si éste no lo escribe, debe usar el constructor predeterminado. Siempre se llama al constructor del nivel superior además de llamar al constructor subordinado.

Llamada a constructores de nivel superior

Puede llamar a un determinado constructor de una superclase como parte de la inicialización de una subclase utilizando la palabra clave super en la primera línea del constructor de la subclase. Es preciso suministrar los argumentos adecuados a super().

Cuando no hay ninguna llamada a super con argumentos, se llama implícitamente al constructor de la superclase que tenga cero argumentos. Si en la superclase no hubiera un constructor sin argumentos se produciría un error de compilación.

La llamada super() puede adoptar cualquier cantidad de argumentos adecuados para los distintos constructores disponibles en la superclase, pero debe ser la primera sentencia del constructor.

Recordar:

  • Si se utilizan, es preciso que super o this sean la primera línea del constructor.
  • Si un constructor no contiene ninguna llamada a super(…) ni a this(…), el compilador introduce una llamada al constructor de la superclase sin argumentos.
  • Otros constructores pueden llamar también a super(…) o this(…), con lo que iríamos encadenando llamadas a constructores. Al final el constructor de la clase de nivel superior se ejecutará antes que ningún otro constructor subordinado.
  • Un constructor nunca puede tener ambas llamadas super(…) y this(…).


 
1 comentario

Publicado por en 9 mayo, 2011 en Estudio, Tema 2

 

Etiquetas: , , , ,

Orientación a objetos (II)

Polimorfismo

Hemos hablado anteriormente del polimorfismo cuando hablabamos sobre la herencia. Decíamos que una variable es polimórfica porque puede hacer referencia a objetos de distintas formas.

Y vimos que Java permitía hacer referencia a un objeto con una variable del tipo de una superclase.
Empleado e = new Tecnico();

Usando la variable e podíamos acceder únicamente a las partes del objeto que son componentes de Empleado (la superclase). Esto se debía a que para el compilador la variable e es de tipo Empleado y no Tecnico.

Pero supongamos que ambas clases tienen un método toString().
Sabemos que si declaramos lo siguiente y llamamos a toString()

Empleado e1 = new Empleado();
Tecnico e2 = new Tecnico();

e1.toString();
e2.toString();

e1 llama al método toString() de la clase Empleado y e2 llama al método toString de la clase Tecnico.

¿Pero que ocurre si hacemos esta llamada?

Empleado e3 = new Tecnico();
e3.toString();

Se obtiene el comportamiento asociado al objeto al que hace referencia la variable durante el tiempo de ejecución. El comportamiento no está determinado por el tipo de la variable en el momento de la compilación. Este es un aspecto del polimorfismo que a menudo recibe el nombre de llamada a métodos virtuales.

e3.toString() llama al método del tipo real del objeto, Tecnico.

Colecciones hetereogéneas

Es posible crear colecciones de objetos que son de una misma clase. Estas colecciones se llaman homogéneas. Por ejemplo, cualquier array que construyamos de una clase es una colección homogénea:

Clientes[ ] clientesNuevos = new Clientes[100];
clientesNuevos[0] = new Cliente();
clientesNuevos[1] = new Cliente();
clientesNuevos[2] = new Cliente();

Sin embargo, Java permite crear colecciones compuestas por objetos de distintos tipos. Estas colecciones se llaman hetereogéneas.

Por ejemplo, una colección con objetos de las subclases de Empleado que vimos anteriormente:
Empleado[] empleados = new Empleado[3];
empleados[0] = new Tecnico();
empleados[1] = new Secretario();
empleados[2] = new Contable();

Es incluso posible hacer una colección con objetos de cualquier clase, ya que en Java todos los objetos heredan de Object. Podemos tener una colección como esta:

Object  [] objetos = new Object[5];
objetos[0] = new Empleado();
objetos[1] = new Batman();
objetos[2] = new Libro();
objetos[3] = new String(“Maria”);
objetos[4] = new Bicicleta();

Podríamos recorrer el array y llamar a un método que fuera común, por ejemplo toString().


Sobrecarga de métodos

En algunos casos podemos necesitar definir varios métodos que realicen la misma función pero con diferentes parámetros. Java permite reutilizar un mismo nombre de método para varios métodos si al llamarlos es posible distinguir unos y otros.

Reglas de los métodos sobrecargados:

  • Las listas de argumentos deben ser diferentes. Deben poder determinar sin ambigüedad qué método es el que se quiere llamar.
  • Los tipos de retorno pueden ser diferentes. El tipo de retorno puede ser diferente pero no es suficiente si ésta es la única diferencia.
  • El modificador de acceso puede cambiar.
  • Puede declarar nuevas o más amplias excepciones comprobadas.

Y a tener en cuenta:

  • Un método se puede sobrecargar en la misma clase o en una subclase.


Llamadas a métodos sobrecargados

Cuando llamamos a un método sobrecargado, existe más de un método con el mismo nombre. Decidir qué método es el que se debe llamar se hace en base a los argumentos.

Si tenemos un método sumar(int a, int b), y otro método sumar(double a, double b).
La llamada sumar(10, 7) llama a sumar con argumentos enteros y la llamada sumar(2.7, 2.3) llama a sumar con argumentos double.

¿Pero qué ocurre cuando en lugar de parámetros de tipos primitivos tenemos parámetros de tipos de referencia?

Supongamos estos métodos sobrecargados de la clase Proyecto.

public void añadirPersonal (Empleado e);
public void añadirPersonal (Tecnico t);

Con estas llamadas:
Empleado e = new Empleado();
Tecnico t = new Tecnico();
Empleado tec = new Tecnico();

e.añadirPersonal(e); //Como esperamos e es de tipo Empleado y llama a añadirPersonal(Empleado e)
e.añadirPersonal(t); //Como es de esperar, t es de tipo Tecnico y llama a añadirPersonal(Tecnico t)
e.añadirPersonal(tec); //En este caso tec es una referencia de Empleado pero es en realidad un objeto Tecnico.

En este ultimo caso se llama al método añadirPersonal(Empleado e).

Aunque el objeto en tiempo de ejecución sea un Tecnico, la decisión de a qué método llamar no se hace dinámicamente en tiempo de ejecución, asi que el tipo de referencia determina a qué metodo sobrecargado se llama.

 
1 comentario

Publicado por en 8 mayo, 2011 en Estudio, Tema 2

 

Etiquetas: , , , ,

 
A %d blogueros les gusta esto: