miércoles, 9 de junio de 2010

Sobreescritura, Sobrecarga y Constructores: Conceptos




Sobreescritura, Sobrecarga y Constructores: Conceptos


La sobrescritura de métodos es una característica que ofrecen lenguajes orientados a objetos, que permite a una clase hija (subclase) implementar a su forma un comportamiento heredado de su clase padre (superclase). Es bueno recordar que para que haya sobrescritura la subclase debe heredar los miembros de la superclase, es decir, no puede haber sobrescritura sin herencia.

El método a ejecutar se determina en tiempo de ejecución y depende del objeto de donde se invoque, es obvio que si se crea un objeto de la superclase, el método a ejecutar será el de la superclase y no el de la clase hija o subclase, mientras que si se crea un objeto de la clase hija con el tipo de referencia de el mismo o de la clase padre, el método que se ejecutara será el método implementado por la subclase en caso de que lo haya sobrescrito.

Existen algunas cosas que saber al momento de sobrescribir métodos, aunque no es nuevo para algunos, esto es solo un review o repaso de las más destacadas:

·         El modificador de acceso del método que sobrescribe no puede ser más restrictivo que el de  la superclase, en cambio se permite lo contrario.
·         El tipo de retorno debe coincidir o en su defecto debe ser un suptipo del tipo declarado en la firma del método de la superclase, esto es tipo covariant.
·         La lista de argumentos entre ambos métodos, el de la subclase y superclase, deben coincidir o de lo contrario lo que se está haciendo es sobrecargando el método.
·         Si el método de la superclase esta marcado con el keyword final, quiere decir que las subclases de esta no pueden sobrescribirlo, es decir, con la palabra reservada final se asegura que un método no sea sobrescrito en demás clases hijas.
·         En caso de que en el método que sobrescribe o en algún otro lugar en el código, se necesite el método de la superclase solo hay que hacer referencia a él con el keyword super. Ej: super.metodoSobrescrito();

La sobrecarga de métodos es otra característica de la programación orientada a objetos, la cual nos permite utilizar el mismo nombre de un método, ya sea que este método este en la misma clase o en la superclase en tal caso, con diferentes argumentos. Sobrecarga es reutilización del identificador del método para provecho del usuario que utiliza nuestra clase, le proporciona al usuario la opción o posibilidad de utilizar el mismo método con diferentes datos de entrada (argumentos).

También en la sobrecarga existen algunas cosas que recordar:

·         Para que haya sobrecarga, es obligatorio el cambio en los argumentos, ya sea de cantidad o de tipos de argumentos.
·         A diferencia de los métodos que sobreescriben, los métodos que sobrecargan se les puede cambiar el modificador de acceso como uno quiera.
·         Tampoco hay restricciones en cuanto al tipo de retorno, es decir, pueden no ser iguales.
·         Cual método sobrecargado a ejecutar se decide en base a los argumentos que este recibe, por eso es la obligatoriedad del cambio en los argumentos.
·         Que método sobrecargado se ejecuta en un momento se determina en tiempo de compilación y no en tiempo de ejecución como los métodos sobreescritos, además este depende del tipo de referencia del objeto y no del objeto instanciado.
·         La sobrecarga y el polimorfismo de ninguna forma se relacionan, al menos en el sentido en el que la sobrescritura lo hace con este último.

Personalmente, antes creía que la sobrecarga era polimorfismo porque en algún otro libro lo había leído así, mas sin embargo, luego de leer este libro el de scjp 6 y otros, además de la edificante clase del profesor y comentarios de compañeros sobre este tema me di cuenta que no, que la sobrecarga es solo reutilización de nombre de métodos. Creo que es bueno pensar como lo hacía Aristóteles: “El ignorante afirma, el sabio duda y reflexiona”.

Bueno los constructores no son más que el código que se ejecuta al momento de crear o un objeto, instanciar nuestra clase. Se utilizan para inicializar el estado de nuestro nuevo objeto. Por estado, entiéndase variables de instancia.

De los constructores se puede decir que:

·         Se ejecutan cuando creamos un objeto mediante la palabra new.
·         No se heredan, y por tanto no se sobrescriben.
·         Se pueden sobrecargar.
·         Pueden tener cualquiera de los modificadores de acceso (public, protected, incluso private).
·         No tienen tipo de retorno, esto es lo que lo diferencia especialmente con los métodos normales.
·         Solo se invocan con el keyword new y/o dentro de otros constructores utilizando alguno de estos (this(), super()).
·         No pueden ser marcados static o final.
·         Deben tener el mismo nombre que la clase.
·         Si no se digita ningún constructor, el compilador introduce uno por defecto; uno que no tiene argumentos.

Creo que todo esto se explica mejor con un pequeños ejemplo:

// Clase Coordenada
public class Coordenada {

            private double abscisa;
            private double ordenada;
           
           
            public Coordenada() {
                        this(15.0, 25.0);                     
            }
           
            public Coordenada(double x, double y) {
                        this.abscisa = x;
                        this.ordenada = y;
            }
           
           
            // getters
           
            public double getAbscisa() {
                        return abscisa;
            }
           
            public double getOrdenada() {
                        return ordenada;
            }
                       
           
            // setters
           
            public void setAbscisa(double x) {
                        this.abscisa = x;
            }
           
            public void setOrdenada(double y) {
                        this.ordenada = y;
            }

}

// Clase FormaGeometrica
public class FormaGeometrica {
           
            private String tipoForma;
            private Coordenada punto;
           
            // Constructores
           
            public FormaGeometrica() {
                        this("FormaGeometrica", new Coordenada(50.0, 50.0));
            }
           
            public FormaGeometrica(String tipo, Coordenada punto) {
                        this.tipoForma = tipo;
                        this.punto = punto;
            }
           
           
            // getter
           
            public String getTipoForma() {
                        return tipoForma;
            }
           
            public Coordenada getPunto() {
                        return punto;
            }
           
           
            // setter
           
            public void setTipoForma(String tipo) {
                        this.tipoForma = tipo;
            }
           
            public void setPunto(Coordenada pto) {
                        this.punto = pto;
            }
           
           
            // ej de sobrescritura
            public void dibujar() {  //  este método se sobrescribirá y sobrecargara en la clase hija Circulo y demas
                        System.out.println("Me dibujo como una forma en general");
            }          
           
            public double calcularArea() {
                        System.out.println("Yo calculo el area de una forma geometrica en general");
                        return 0.0;
            }          
}


// Clase Circulo
public class Circulo extends FormaGeometrica {
           
            //private Coordenada punto;
            private double radio;
           
           
            // Constructores
           
            public Circulo() {
                        this(new Coordenada(15.0, 25.0), 5.0);
            }
           
            public Circulo(Coordenada pto, double radio) {
                        super("Circulo", pto);
                        this.radio = radio;
            }
           
           
            public double getRadio() {
                        return radio;
            }
           
            public void setRadio(double radio) {
                        this.radio = radio;
            }
           
            // gral methods
           
            public void dibujar() { // método sobrescrito
                        System.out.println("Me dibujo como solo un circulo lo puede hacer.");
                        this.dibujar(this.getPunto());
            }

            public void dibujar(Coordenada pto) { // ejemplo de sobrecarga
                        System.out.printf("Circulo dibujandose en el punto %.2f, %.2f del espacio. \n", pto.getAbscisa(), pto.getOrdenada());
            }
           
            public double calcularArea() { // este metodo devuelve el area de el mismo
                        return this.calcularArea(this.radio);
            }
           
            public double calcularArea(double radio) { // este metodo devuelve el area de cualquier circulo
                        return Math.PI * (radio * radio);
            }
}

// Clase Triangulo
public class Triangulo extends FormaGeometrica {

            private double ladoA;
            private double ladoB;
            private double ladoC;
           
            // constructores
           
            public Triangulo() {
                        this(new Coordenada(15.0, 40.0), 5.0, 5.0, 5.0);
            }
           
            public Triangulo(Coordenada pto, double lA, double lB, double lC) {
                        super("Triangulo", pto);
                        this.ladoA = lA;
                        this.ladoB = lB;
                        this.ladoC = lC;                     
            }
           
           
            // getters
           
            public double getLadoA() {
                        return ladoA;
            }
           
            public double getLadoB() {
                        return ladoB;
            }
           
            public double getLadoC() {
                        return ladoC;
            }
           

            // setters
           
            public void setLadoA(double lado) {
                        this.ladoA = lado;
            }
           
            public void setLadoB(double lado) {
                        this.ladoB = lado;
            }
           
            public void setLadoC(double lado) {
                        this.ladoC = lado;
            }
           
           
            // gral methods
           
            public void dibujar() { // método sobrescrito
                        System.out.println("Me dibujo como solo un triangulo lo puede hacer.");
                        this.dibujar(this.getPunto());
            }

            public void dibujar(Coordenada pto) { // ejemplo de sobrecarga
                        System.out.printf("Triangulo dibujandose en el punto %.2f, %.2f del espacio.\n", pto.getAbscisa(), pto.getOrdenada());
            }
                       
            public double calcularArea() { // este metodo devuelve el area de el mismo
                        return this.calcularArea(this.ladoA, this.ladoB, this.ladoC);
            }
           
            public double calcularArea(double lA, double lB, double lC) { // este metodo devuelve el area de cualquier circulo
                        // segun el Matematico griego Heron, la formula para calcular el area de cualquier triangulo es AT = raiz cuadrada de ( S ( S-A )( S-B )( S-C ) ) 
                        // *** S = semiperimetro = ( A + B + C ) / 2
                       
                        double semiPerimetro = (lA + lB + lC) / 2;
                        double area = Math.sqrt(semiPerimetro * ((semiPerimetro - lA) * (semiPerimetro - lB) * (semiPerimetro - lC)));
                       
                        return area;
            }
           
}

// Clase Cuadrado
public class Cuadrado extends FormaGeometrica {
           
            private double lado;
           
            // constructores
           
            public Cuadrado() {
                        this(new Coordenada(15.0, 55.0), 5.0);
            }

            public Cuadrado(Coordenada pto, double lado) {
                        super("Cuadrado", pto);
                        this.lado = lado;
            }
           
           
            // getter
           
            public double getLado() {
                        return lado;
            }
           
           
            // setter
           
            public void setLado(double lado) {
                        this.lado = lado;
            }
           
           
            // gral methods
           
            public void dibujar() { // método sobrescrito
                        System.out.println("Me dibujo como solo un Cuadrado lo puede hacer.");
                        this.dibujar(this.getPunto());
            }

            public void dibujar(Coordenada pto) { // ejemplo de sobrecarga
                        System.out.printf("Cuadrado dibujandose en el punto %.2f, %.2f del espacio.\n", pto.getAbscisa(), pto.getOrdenada());
            }
           
            public double calcularArea() { // este metodo devuelve el area de el mismo
                        return this.calcularArea(this.lado);
            }
           
            public double calcularArea(double lado) { // este metodo devuelve el area de cualquier circulo
                        return lado * lado;
            }
           
}

// Clase TestFormaGeometrica
public class TestFormaGeometrica {

            public static void main(String[] args) {
                        System.out.println("\n.... Probando Sobrescritura, Sobrecarga y Constructores ....\n");
                        FormaGeometrica[] formasGeometricas = new FormaGeometrica[] {new Circulo(), new Cuadrado(), new Triangulo()};
                        new TestFormaGeometrica().testFormas(formasGeometricas);
            }

            public void testFormas(FormaGeometrica[] formas) {
                        for (FormaGeometrica fg : formas) {
                                   System.out.printf("Forma: %s\n", fg.getTipoForma());
                                   fg.dibujar();
                                   System.out.printf("Mi Area: %.2f\n\n", fg.calcularArea());
                        }
            }
           
}


Salida de la aplicación TestFormaGeometrica:

.... Probando Sobrescritura, Sobrecarga y Constructores ...

Forma: Circulo
Me dibujo como solo un circulo lo puede hacer.
Circulo dibujandose en el punto 15.00, 25.00 del espacio.
Mi Area: 78.54

Forma: Cuadrado
Me dibujo como solo un Cuadrado lo puede hacer.
Cuadrado dibujandose en el punto 15.00, 55.00 del espacio.
Mi Area: 25.00

Forma: Triangulo
Me dibujo como solo un triangulo lo puede hacer.
Triangulo dibujandose en el punto 15.00, 40.00 del espacio.
Mi Area: 10.83


 --


Edwin A. Bratini
Computer Science Student - UASD
Cel.: 809-705-6361
edwin.bratini@gmail.com
edwin_bratini@hotmail.com

jueves, 3 de junio de 2010

Encapsulacion, Herencia y Polimorfismo



Encapsulación, Herencia y Polimorfismo: Conceptos Elementales e Importantes en el paradigma de la Programación Orientada a Objetos.


La Encapsulación es el ocultamiento de las propiedades o estado de los datos miembro de un objeto, de tal forma que la única vía para acceder a estos se a través de métodos o comportamientos públicos, estos últimos constituyen la interfaz entre el objeto y el usuario.






La encapsulación se utiliza para proteger los datos de posibles modificaciones de entidades que no tienen permiso para hacerlo, o de modificaciones controladas y/o no esperadas. También para asegurar el buen funcionamiento de demás código que utilice el objeto si la implementación de este llegue a cambiar.

Un ejemplo de encapsulación es el funcionamiento de un maquina que despacha refrescos o chocolates, en la que esta recibe una moneda hace algún proceso de verificación y otras cosas y devuelve un refresco. Nosotros como usuarios de la maquina (objeto) no necesitamos saber como hace este proceso, sino por donde se introducen las monedas (método introducirMonedas()) y por donde retiramos lo que queremos de la maquina (método getElemento()). Estos últimos métodos o comportamientos son la interface entre el objeto y nosotros los usuarios.

En lo que nos compete, en Java conseguimos encapsulación cuando en una clase tenemos las variables de instancia protegidas del exterior, a través de los modificadores de acceso (prívate, protected, default) y cuando creamos métodos públicos para acceder a estas variables.


Ej.:

// Clase Carro
public class Carro {

  private String color;
  private String marca;
  private double velocidad;
 
  public Carro() {
    super("Carro");
  }

// getters

  public String getColor() {
    return color;
  }

  public String getMarca() {
    return marca;
  }

  public double getVelocidad() {
    return velocidad;
  }

// setters

  public void setColor(String color) {
    this.color = color;
  }

  public void setMarca(String marca) {
    this.marca = marca;
  }
 
  public void setVelocidad(double velocidad) {
    this.velocidad = velocidad;
  }

  // gral methods

  public void acelerar() {
    System.out.println("... Acelerando ...");
  }
 
  public void mover() {
    System.out.println("... Me muevo como un carro; en la tierra ...");
  }

}


La Herencia es una expresión del polimorfismo que promueve la reutilización de código, en el que una subclase hereda comportamientos y características de otra clase llamada superclase.




En el concepto de herencia de distinguen las relaciones is-a y has-a entre objetos. En la primera un objeto de una subclase puede ser tratado o como el mismo o como un objeto de su superclase/interface (polimorfismo), la segunda se manifiesta cuando dentro de una clase tenemos como variables de instancias a referencias a otros objetos.

En java una subclase extiende o hereda de otra clase a través del keyword extends, e implementa comportamientos de una interface utilizando el keyword implements

Ej.:

// Clase Vehiculo
public class Vehiculo {
  private String tipo;
 
  public Vehiculo() {
  }

  public Vehiculo(String tipo) {
    this.tipo = tipo;
  }

  public String getTipo() {
    return tipo;
  }

  public void mover() {
    System.out.println("... Me muevo como un vehiculo en general ...");
  }

}

// Interface DeCarreras
public interface DeCarreras {
  public void setNitrogeno();
}


// Nueva Clase Carro
public class Carro extends Vehiculo implements DeCarreras {
  private String color;
  private String marca;
  private double velocidad;

  public Carro() {
    super("Carro");
  }

// getters

  public String getColor() {
    return color;
  }

  public String getMarca() {
    return marca;
  }
 
  public double getVelocidad() {
    return velocidad;
  }


// setters

  public void setColor(String color) {
    this.color = color;
  }

  public void setMarca(String marca) {
    this.marca = marca;
  }

  public void setVelocidad(double velocidad) {
    this.velocidad = velocidad;
  }


// gral methods

  public void acelerar() {
    System.out.println("... Acelerando ...");
  }

  public void setNitrogeno() {
    System.out.println("\t... Nitrogeno suelto ...");
  }

  public void mover() {
    System.out.println("... Me muevo como un carro; en la tierra ...");
  }

}

Por ejemplo la clase Carro extiende o hereda directamente de la clase vehiculo e implementa la interface DeCarreras. La clase Carro hereda los métodos mover y getTipo de su superclase vehiculo y sobre escribe el método setNitrogeno de la interface.

El Polimorfismo es un concepto de programación orientado a objetos que nos permite escribir programas que utilizan objetos que comparten la misma superclase en la jerarquía de clases. Es decir en un momento determinado dependiendo de la lógica de nuestro código, podemos tratar un objeto de diferentes formas (polimorfismo) como el mismo o como un objeto de la clase que hereda o interface que implementa, cual sea el caso. El polimorfismo está estrechamente relacionado con la herencia y la sobre escritura.

Ej.: Supongamos que tenemos dos clases más a parte de la clase Carro que extienden de la clase Vehículo de más arriba

// Clase Tren
public class Tren extends Vehiculo{
  public Tren() {
    super("Tren");
  }

  public void mover() {
    System.out.println("... Me muevo como un tren; en rieles ...");
  }

  public void hacerChuChu() {
    System.out.println("... Chu-Chu ...");
  }

}


// Clase Avion
public class Avion extends Vehiculo {
  public Avion() {
    super("Avion");
  }

  public void mover() {
    System.out.println("... Me muevo como un avion; en el aire ...");
  }

  public void aterrizar() {
    System.out.println("... Aterrizando ...");
  }

}


// Clase TestVehiculo
public class TestVehiculo {

  public static void main(String[] args) {
    System.out.println("\n.... Probando Conceptos de Encapsulacion, Herencia y Polimorfismo ....\n");
    Vehiculo[] vs = new Vehiculo[] {new Carro(), new Avion(), new Tren()};
    new TestVehiculo().testMedioTransporte(vs);
  }

  public void testMedioTransporte(Vehiculo[] vhcls) {
    for (Vehiculo v : vhcls) {
      System.out.print(v.getTipo() + ": ");
      v.mover();
      if (v instanceof Carro) {
        ((Carro)v).setNitrogeno();
      }
    }
  }

}


Cada subclase de este ejemplo que extiende de la clase Vehiculo hereda y sobre escribe el método mover, ya que cada uno de estos se puede mover de diferentes formas (en rieles, en la tierra o en el aire).

En la aplicación java de ejemplo (TestVehiculo), se simula el movimiento de cada una de estas clases, la aplicación le envía el mismo mensaje a cada objeto, el de moverse y estos dependiendo de la implementación reaccionan diferente o de varias formas (polimorfismo). Confiando en cada objeto la forma en que pueda implementar un mismo comportamiento, es el concepto fundamental del polimorfismo. En esta aplicación se utilizó el concepto de interface y conversión de tipos.

Cuando se ejecuta la aplicación TestVehiculo el resultado es:

%java java_code.scjp6.TestVehiculo

.... Probando Conceptos de Encapsulacion, Herencia y Polimorfismo ....

Carro: ... Me muevo como un carro; en la tierra ...
          ... Nitrogeno suelto ...
Avion: ... Me muevo como un avion; en el aire ...
Tren: ... Me muevo como un tren; en rieles ...



Edwin A. Bratini
Computer Science Student - UASD
Cel.: 809-705-6361
edwin.bratini@gmail.com
edwin_bratini@hotmail.com