Herencia y Polimorfismo - Java

Publicado en 'Programación' por AiApaec, 11 Feb 2015.





  1. AiApaec

    AiApaec Miembro frecuente

    Registro:
    1 May 2014
    Mensajes:
    58
    Likes:
    14




    Hola foreros, esto es una respuesta a una duda a uno de los foristas que están empezando en programación, lo pongo acá para que lo aproveche a quién le sirva.

    La idea herencia es es para heredar comportamiento, reutilizarlo sin duplicar código; por ejemplo digamos que tú tienes
    la habilidad de leer la mente y que tu hijo herede esa habilidad, tu hijo también podrá leer la mente, él puede desarrollar otra
    habilidad, no tiene que esforzarse para aprender a leer la mente, la trae por defecto, se la heredó su padre, ahora solo tiene
    que preocuparse por desarrollar otra habilidad, por ejemplo volar; ahora tu hijo tiene dos habilidades y decide heredar esas
    dos habilidades a tu nieto, el nieto tendrá esas dos habilidades por defecto, no tendrá que preocuparse en desarrollarla, ahora
    éste puede pensar en desarrollar otra habilidad, por ejemplo teletransportarse. Y así pueden heredar y aumentar sus habilidades.
    Fíjate que tu hijo solo se preocupó de una habilidad, sin embargo tenía dos; tu nieto también se preocupó de una sola habilidad,
    sin embargo tenía tres.
    Ahora traslademos eso a la OOP (programación orientada a objetos). Digamos que tienes que representar varios vehículos
    y tipos de vehículos, por ejemplo un automóvil y un camión. Podríamos hacer dos clases:

    PHP:
    public class Automovil{
         
            public 
    void acelerar(){
                    
    //código necesrio para acelerar...
            
    }
            public 
    void desacelerar(){
                    
    //código necesrio para desacelerar...
            
    }
            public 
    void frenar(){
                    
    //código necesrio para frenar...
            
    }    
            public 
    void apagar(){
                    
    //código necesrio para apagar...
            
    }
         
    }

    public class 
    Camion{
         
            public 
    void acelerar(){
                    
    //código necesrio para acelerar...
            
    }
            public 
    void desacelerar(){
                    
    //código necesrio para desacelerar...
            
    }
            public 
    void frenar(){
                    
    //código necesrio para frenar...
            
    }    
            public 
    void apagar(){
                    
    //código necesrio para apagar...
            
    }
         
    }
    Nos damos cuenta ambos al ser vehículos tienen el mismo comprtamiento, solo se diferencian en algunas cosas. Se nos podría
    ocurrir esta implementación:
    PHP:
    public class Vehiculo{
         
            public 
    String _tipoVehiculo;
         
            public 
    Vehiculo(String tipoVehiculo){
                    
    _tipoVehiculo tipoVehiculo;
            }
         
            public 
    void acelerar(){
                    
    //código necesrio para acelerar...
            
    }
            public 
    void desacelerar(){
                    
    //código necesrio para desacelerar...
            
    }
            public 
    void frenar(){
                    
    //código necesrio para frenar...
            
    }    
            public 
    void apagar(){
                    
    //código necesrio para apagar...
            
    }
         
    }
    No nos sirve porque ¿qué pasa si el algoritmo o la implementación o el código necesario para el método frenar() es diferente entre el camión y el automóvil?
    Por ejemplo el frenado del automóvil solo puede requerir "n" pasos, pero el frenado del camión "n+1" pasos, es decir necesitamos
    hacer dos veces el método frenar(), uno para el automóvil y otro para el camión, esto por cuanto son diferentes. Otra solución
    sería poner varios "if" o "case" para verificar y cuando sea automóvil que haga "esto" y cuando sea camión que haga "lo otro"; sin
    embargo esta solución es mala, se debe evitar porque ¿que pasaría si después necesitamos un nuevo vehículo, por ejemplo un tractor?, pues tendríamos
    que ir al método frenar() y actualizar el código, y haríamos eso cada vez que necesitemos un nuevo vehículo.

    Aquí nos puede ayudar la herencia entre clases, una clase que hereda de otra, hereda los miembros públicos(public) y protegidos(protected) de esa otra clase, es decir
    tiene acceso a ellos. La clase que hereda se denomina subclase/clase hija/clase derivada/clase extendida y la clase de la que se hereda se denomina
    super clase/clase padre/clase base. En otras palabras Una subclase extiende a una superclase o una clase hija hereda de una clase padre, puedes jugar con los
    términos.

    Así que identificamos los comportamientos comunes entre los vehículos y ponemos en la clase base o superclase esos comportamientos comunes que deseamos que estén presentes en los
    demás vehículos:
    PHP:
    public abstract class Vehiculo{
         
            public 
    void acelerar(){
                    
    //código necesrio para acelerar...
            
    }
            public 
    void desacelerar(){
                    
    //código necesrio para desacelerar...
            
    }
         
            public abstract 
    void frenar();
         
            public 
    void apagar(){
                    
    //código necesrio para apagar...
            
    }    
    }

    public class 
    Automovil extends Vehiculo{
         
            @
    Override
            
    public void frenar(){
                    
    //Implementación para el frenado de un automóvil
            
    }    
    }

    public class 
    Camion extends Vehiculo{
            @
    Override
            
    public void frenar(){
                    
    //Implementación para el frenado de u camión
            
    }    
    }
    Como ves la super clase es Vehiculo y las clases hijas son Automovil y Camion. Ahora puedes desde la instancia de cualquiera de esas dos clases puedes acceder
    a los métods acelerar(), desacelerar() y apagar(), y como ves esas clases hijas no tienen ningún código para esos métodos, es porque ya lo están heredando y
    son accesibles por el modificador de acceso "public" que tienen (public void...) en la super clase. Por ejemplo puedes hacer lo siguiente sin ningún problema:
    PHP:
    Camion c = new Camion();
    c.acelerar(); //método implementado en la super clase.
    c.frenar(); //método implementado en la clase hija

    Automovil a = new Automovil();
    a.desacelerar();/método implementado en la super clase.
    a.frenar(); //método implementado en la clase hija
    Una cosa, cuando digo implementado me refiero a que tiene código, por ejemple en la superclase el método frenar() no está implementado, solo está la firma del
    método, en los otros tres métodos si están implementados. En las clases hijas es donde se implementa el método frenar(), es decir se escribe el código correspondiente.

    Hay dos cosas que talvez te parezcan nuevas, y son las palabras clave "abstract" y "override". Bien cuando una clase es abstracta, como el caso de Vehiculo, ella no
    es instanciable, es decir no puedes hacer esto sin que el compilador dé error:
    PHP:
    Vehiculo v = new Vehiculo(); //error, las clases abstractas no son instanciables.
    Sin embargo las clases abstractas son heredables o extendibles; como vemos, las clases Camion y Automovil heredan de una clase abstracta pero no son
    clases abstractas, por lo tanto sí son instanciables.

    Un método abstracto solo consta de su firma, como se ve en la clase Vehiculo, y debe ser implementada en las clases derivadas (clases que hereden de Vehiculo),
    pero para implementar un método abstracto se usa la "anotación" "Override", con eso se especifica que se está implementando el método declarado en la superclase.

    En el ejemplo, por simplicidad estamos partiendo de que ambos vehículos, el automóvil y el camión, hacen exactamente lo mismo cuando van a "acelerar", "desacelerar"
    y "apagar", por eso usan la misma implementación. Ahora digamos que necesitamos un tractor y que pasa lo mismo, todo es igual excepto el frenado, lo que
    tendríamos que hacer es:
    PHP:
    public class Tractor extends Vehiculo{
            @
    Override
            
    public void frenar(){
                    
    //Implementación para el frenado de un tractor
            
    }    
    }
    Como ves se trata de una implementación elegante, en vez de estar con "if" y "case" que crean un código de mala calidad ya que viola el principio "Open closed principle", es decir
    que una clase debe estar abierta para su extensión pero cerrada para su modificación (lo mejor es no tocar lo que ya está hecho y testeado y funcionando, modificar
    ese código puede resultar en errores futuros y/o en otras partes de la aplicación, etc. Es una de las rozones de ese principio (son varios principios pero eso ya lo verás más adelante).

    Además, la herencia tambien permite el "Polimorfismo", me gusta la definición de la wikipedia en español:

    "el polimorfismo se refiere a la propiedad por la que es posible enviar mensajes sintácticamente iguales a objetos de tipos distintos."

    Pensemos en estos objetos de distintos tipos:
    PHP:
    Automovil a = new Automovil();
    Camion c = new Camion();
    Ahora pensemos en que nuestra aplicación ya está desarrollada y ha estado trabajando con esos dos tipos de vehículos, pero ahora nos pidieron agregar uno nuevo,
    el tractor. Imaginemos que hay una clase en otro package o librería que se encarga de manejar los vehiculos. Un diseño muy malo de esa clase sería:

    PHP:
    public class Conductor{
        
            public 
    void acelerarCamion(Camion camion){
                    
    camion.acelerar();
            }
        
            public 
    void acelerarAutomovil(Automovil automovil){
                    
    automovil.acelerar();
            }
    }
    Habria que ir a ese otro package y modificar la clase, agregar el método "acelerarTractor()" y recompilar. Eso es un problema y un diseño pobre.
    El Polimorfismo viene al rescate, desde un principio se hubiera diseñado de esta forma:
    PHP:
    public class Conductor{
            public 
    void acelerar(Vehiculo vehiculo){
                    
    vehiculo.acelerar();
            }    
    }
    Podemos crear las clases derivadas de Vehiculo que queramos y no tendremos que modificar la clase que trabaja con los vehiculos, la clase Conductor.
    Es decir, se puede hacer esto sin ningún problema:
    PHP:
    Vehiculo a = new Automovil();
    Vehiculo c = new Camion();
    Vehiculo t = new Tractor();

    Conductor conductor = new Conductor();
    conductor.acelerar(a);//acelera el automóvil
    conductor.acelerar(c);//acelera el camión
    conductor.acelerar(t);//acelera el tractor.
    Eso es polimorfismo.

    PD: Por ciero, todas las clases heredan implícitamente de Object. Por eso cuando creas una clase puedes hacer esto sin ningún problema: (el método "toString()" esta implementado en la clase Object, la cual vendría a ser una superclase)
    PHP:
    MiClase mc MiClase();
    String strClase mc.toString();
    :hi:
     
    Última edición: 11 Feb 2015
    A galessandro y Jonaaaa les gustó este mensaje.


  2. Jonaaaa

    Jonaaaa Miembro frecuente

    Registro:
    27 Jul 2013
    Mensajes:
    99
    Likes:
    3
    Muchas gracias Muy interesante , espero puedas crear temas así para que nos ayudes a los novatos o refrescarnos la memoria , seria
    Muy interesnte gracias
     
  3. imeireparo

    imeireparo Miembro de bronce

    Registro:
    8 Ene 2015
    Mensajes:
    2,008
    Likes:
    168
    No entender ?
     
  4. FulioMG

    FulioMG Miembro de plata

    Registro:
    15 Dic 2014
    Mensajes:
    3,555
    Likes:
    970
    buen aporte para los cachorritos :D
     
  5. perchi18

    perchi18 Miembro maestro

    Registro:
    23 Set 2012
    Mensajes:
    877
    Likes:
    117
    A la parece que lo hizo uno de los X - men
     
  6. drkrap

    drkrap Miembro de bronce

    Registro:
    18 Ene 2011
    Mensajes:
    1,345
    Likes:
    304
    entiendo que no sera el unico post.. esta bueno
     
  7. galessandro

    galessandro Miembro frecuente

    Registro:
    21 Mar 2012
    Mensajes:
    231
    Likes:
    26
    excelente post, sigue asi!!
     
  8. ricardoegz

    ricardoegz Miembro de plata

    Registro:
    30 Dic 2012
    Mensajes:
    3,154
    Likes:
    582
    Siempre es bueno una refrescada de memoria.
    Excelente post!!
     
  9. Jmm

    Jmm Miembro frecuente

    Registro:
    14 Set 2011
    Mensajes:
    67
    Likes:
    6
    Se agradece, aunque deberías resumir todo un poco.
     
  10. debugger

    debugger Miembro nuevo

    Registro:
    13 Jun 2014
    Mensajes:
    38
    Likes:
    6
    Muy bueno tu tema, ya me dieron ganas de hacer uno de Hibernate.