style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">

Polimorfismo

El polimorfismo es quizá al característica más importante de la programación orientada a objetos y también quizá la más difícil de entender y creo que en parte se debe al nombre; no ayuda mucho y si impresiona al principio.

Pero si en vez de pensar en la palabra pensamos en como los objetos se comportan de diferente forma dependiendo de su naturaleza puede ser más claro.

Por ejemplo, en el post anterior Clases y Objetos hablaba como los objetos de nuestros sistema se pueden clasificar de acuerdo a sus atributos y comportamiento. Los objetos de la misma clase se comportan igual y los objetos de diferentes clases se comportan diferente. ¿Parece demasiado obvio verdad? Pues es que lo es.

Por ejemplo, el mecanismo de persistencia ( entiéndase, cuando voy a "salvar", o "guardar" algo ) puede utilizar una base de datos o el sistema de archivos entonces se tendrían dos clases diferentes donde cada una hiciera algo distinto.

Si tuvieramos esta clase llamada Persona:

class Persona {
   private String nombre;
   private String apellido;
   public String nombre()  { return this.nombre; }
   public String apellido()  { return this.nombre; }
}

Podríamos escribir una clase que persistiera personas a una base de datos así:

class GuardaPersonaEnBaseDeDatos {
    void salva( Persona persona ) {
          SomeMagicLibrary.executeUpdate(
                 "UPDATE persona SET nombre = ? , apellido = ? ",
                  persona.nombre(),
                  persona.apellido() );
     }
}

El código para salvar a la base de datos no importa mucho en este ejemplo, basta con ver que se utiliza una sentencia SQL y por ahí una biblioteca mágica que haga todo el trabajo.

Podríamos también tener otra clase que en vez de usar una base de datos escribiera a un archivo:

class GuardaPersonaEnArchivo {
    void salva( Persona persona ) {
          PrintStream out = new PrintStream( new FileOutputStream( persona.nombre()  + ".txt") );
          out.printf("{ 'nombre' : '%s',  'apellido' : '%s' }", persona.nombre(), persona.apellido() );
          out.close();
      }
}

De nuevo la implementación aquí no es la importante, sino que tenga un método que recibe una instancia de persona y hace algo.

De esto se trata el polimorfismo! de que diferentes clases responden de diferente forma al mismo mensaje ( mensaje es cuando se invoca o se ejecuta un método ), en este caso el mensaje o el método fue salva( Persona ) y las dos clases lo hicieron de forma diferente. Eso es todo.

Ahora. En Java, el tipo de dato ( la clase ) del objeto se debe de declarar antes de que el programa se ejecute ( por eso se le llama de tipeo estático, por que los tipos de datos se definen "en frio" )

// Declara una variable llamada juan de tipo Persona
Persona juan;

Otros lenguajes permiten definir el tipo de dato en tiempo de ejecución ( tipeo dinámico )

Como a los objetos solo se les pueden enviar mensajes que estén definidos en su propia clase, habría problema para intentar hacer esto:

Persona juan = new Persona();
...

GuardaPersonaEnBaseDeDatos guardar = new GuardaPersonaEnBaseDeDatos();
guardar.salva( juan ); // lo almacena en la base de datos.

guardar = new GuardaPersonaEnArchivo(); // eeerrrk!!  Error en tiempo de compilación
guardar.salva( juan ); // ya ni siquiera llega a esta línea.

El problema es que la variable guardar fue declarada como de tipo GuardaPersonaEnArchivo y por lo tanto ya no se le puede asignar el tipo GuardaPersonaEnArchivo

En Java existen dos formas de solucionar este problema, la primera es definir una clase padre para ambas clases y definir el tipo de dato como el de esa clase:

class Guardador {
   void salva( Persona persona ) {
      // nah... no hagas nada..
    }
}

Y luego

class GuardaPersonaEnBaseDeDatos extends Guardador {
   void salva( Persona persona ) {
     . .. . .
    }
}

y

class GuardaPersonaEnArchivo extends Guardador {
   void salva( Persona persona ) {
     . .. . .
    }
}

Y definir el tipo de datos como el del padre:

Persona juan = new Persona();
...

Guardador guardar = new GuardaPersonaEnBaseDeDatos();
guardar.salva( juan ); // lo almacena en la base de datos.

guardar = new GuardaPersonaEnArchivo(); // compila bien
guardar.salva( juan ); // ejecuta el otro método

Y dadaaaa!!! ya tenemos polimorfismo.

La otra opción es implementando una interfaz pero ya veremos eso después.

Conclusión

Si parece demasiado fácil hacer polimorfirsmo en Java y se quedan con la idea de "Que!! eso es todo?!" pues les diré que sí, eso es todo.

Lo interesante aquí viene en la forma en la que se utiliza el polimorfismo y porque es tan importante en la POO.

Por ejemplo en los plugins se escribe código y se llaman métodos sobre clases que ni siquiera existen aún, porque será el autor del plugin el que provea esa nueva funcionalidad.

De la misma forma, la gran mayoría de los famosos patrones de diseño se basan precisamente en esto, en el polimorfismo para que sean las implementaciones las que digan que es lo que se va a hacer.

Y en el ejemplo mismo que es tan burdo, se puede agregar fácilmente quizá un año después de haber escrito este pedazo de código, un nuevo "Guardador" que utilice la red en vez de la base de datos o el sistema de archivos, bastaría definir el nuevo método:

class GuardaEnRed extends Guardador  {
   void salva( Persona persona ) {
       Socket s = new Socket( someServer, somePort );
       DataOutputStream dos = new  DataOutputStream( s.getOutputStream() );
       dos.writeUTF( persona.nombre() );
       dos.writeUTF( persona.apellido() );
       dos.close();
    }
}

Con lo que tendríamos un nuevo guardador. Y eso es todo.

¿Preguntas?

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de beto.bateria

El polimorfismo es el que le

El polimorfismo es el que le da POWER a la OOP.

Imagen de AlexSnake

Fácil de entender, difícil de digerir

+1 por tu post, es bastante entendible lo que explicas y muy fácil de entender, pero cuando lo quieres llevar a la práctica (al menos a mi me pasa) es cuando es difícil de digerir ya sea porque no estás acostumbrado a programar de esa forma o por que estas empezando a programar. Y ya que lo mencionaste tengo una duda, sí la clase Guardador no tiene código en su método, ¿podría ser una interfaz? Es decir ¿puedo hacer polimorfismo con interfaces?

Un poquitin de abstraccion

En este caso, veo tambien la importancia de resaltar que existe eso de las clases y metodos abstractos. En el ejemplo de oscar se pudo haber definido una interface pero tambien una clase abtracta que defina los metodos que deben de rellenarse para que la implementacion tenga un comportamiento particular.

Me explico:

public abstract class Guardador {
       
        public abstract void salva();
       
}

Definimos una clase Guardador que nos dice que tenemos un metodo abstracto salva() el cual, por ser abstracto lo debemos definir en la clase que herede a Guardador para que de esa forma el comportamiento sea diferente de todas esas implementaciones del metodo salva()

  
Lo que menciono quedaria asi

class SalvaABaseDeDatos extends Guardador {

        @Override
        public void salva () {
                // logica del metodo
        }
       
}

Como estamos haciendo herencia de public void salva (); debemos definir este metodo (con el mismo modificador de acceso, porque asi se definio en la clase abstracta... queda de tarea investigar el ¿por qué?) y dentro de la sobreescritura del metodo ahi es donde le damos la particularidad del comportamiento.

  
Si deseamos tener varias clases con los mismos metodos y que puedan ser declaradas bajo un mismo tipo, esta solución también es viable e incluye el concepto de polimorfismo. Dicho lo anterior entonces sabemos que podemos hacer esto:

Guardador g = new SalvaABaseDeDatos ();

  
  
Para el desarrollo de plugins se maneja mucho el uso de interfaces que va tambien implicito el polimorfismo... "practicamente" es lo mismo que abstraccion, tiene mas limitaciones su uso en definicion de propiedades y de modificadores de acceso, ¿a ver quien se avienta a complementar el ejemplo de Oscar usando interfaces?

Imagen de AlexSnake

ejemplo mas complejo

Polimorfismo: "Llamada a un Java / método virtual utilizando una referencia a una superclase más generalizado de un objeto real invoca el método en el objeto real (el más específicos de subclase), utilizando un bottom-up mecanismo de búsqueda". Noooo pues con estas definiciones si le voy a entendeeeer, al menos con esta definicion me hago mas bolas. y luego este ejemplo que encontre:

void test ()(

//un objeto de tipo gato
  Cat simon = new Cat ();

//se crea un objeto de tipo animal, que extiende de gato ???
  Animal animal = simon; /* upcasting */
  animal.eat (); /* = Cat.eat */

  Perro rover = new Perro ();

   /* Rover segura upcast referencia a los animales*/
  feed(rover);
  feed(simon); //aqui se esta mandando nuevamente al objeto gato
)
void feed(Animal a)
(
   // Animal.eat () = Cat.eat ()
   // feed(rover) = Dog.eat ()
   //feed(Simon) = Cat.eat ()
   a.eat ();
)

Sinceramente no entiendo si se hace las mismas operaciones en  animal.eat(); y cuando se manda al metodo y se ejecuta  a.eat();

No dudo que para muchos se les complique entenderlo, pero tambien a otros como yo pues si se nos dificulta y mas con este tipo de ejemplos.

Imagen de AlexSnake

Hablando de sobreescritura...

Hace unos días tenía que hacer un progressbar que se actualizara conforme un proceso iba avanzando y ezamudio me recomendó utilizar el StringWorker, que mejor ejemplo para ilustrar lo que java.daba.doo menciona ya que tu clase debe extender de la clase StringWorker y pasarle dos parámetros que puede ser de cualquier tipo,  public class MiClase extends SwingWorker<String, Void> y debe sobrescribir el método doInBackground, ¿no sé si eso también sea polimorfismo?

Imagen de beto.bateria

Yo te recomendaria usar

Yo te recomendaria usar interfaces, a excepcion de algunos casos en donde te recomendaria clases Abtractas:

public interface Guardador {
   void salva( Persona persona );
}
class GuardaEnRed implementsGuardador  {
   void salva( Persona persona ) {
       Socket s = new Socket( someServer, somePort );
       DataOutputStream dos = new  DataOutputStream( s.getOutputStream() );
       dos.writeUTF( persona.nombre() );
       dos.writeUTF( persona.apellido() );
       dos.close();
    }
}

Muy bueno, pero algo confuso para novatos

Cuando antes trataba de explicarle a alguien nuevo a la POO comenzaba con ejemplos cómo el que tú das. Al final siempre terminaban preguntándome: "Bueno, a fin de cuentas polimorfismo es..." y con una carota de: "WTFFFF?!?!?!?!?!".

A mi parecer una manera más sencilla de hacer entender el polimorfismo es la manera en que llamas a un mensaje...aunque hay diferencia de opiniones respecto de los operadores (hablando de Java) en si se pueden o no considerar cómo mensajes. Sin embargo ilustran de una manera el polimorfismo. Ejemplo:

// Ilustrando polimorfismo con el signo +
class EjemploSignoSuma{
---public static void main(String ... args){
------//Si usamos el signo + con un entero, lo suma:
------int x = -15;
------int y = 665;
------int z = x + y;
------String s = String.format("Usando el símbolo de suma obtenemos que %d + %d = %d", x, y, z);
------//Ahora un ejemplo del uso del símbolo + con cadenas
------String wordOne = "The only place";
------String wordTwo = "where you can dream";
------s += "\n La frase usando el símbolo + es: " + wordOne + wordTwo;
------System.out.println(s);
------System.exit(0);
---}
}

Y lo vemos fácil, pues para cadenas el símbolo + es igual a decirle: "concatena", y para números es igual a decirle: "suma". Otro ejemplo de polimorfismo con este mismo símbolo es el uso de expresiones regulares (las cuales, un servidor no domina muy bien, de hacer una metida de pata corregirme por favor), en donde este mismo símbolo (+) sirve para indicar que debe haber uno o más dígitos de un carácter indicado.

Bueno, ya está esto bastante largo, hasta ahí lo dejo.

Excepto que + es un operador

Excepto que + es un operador en Java, así que no hay objetos involucrados. :(

Imagen de benek

Re: Muy bueno, pero algo confuso para novatos

Eso es sobrecarga, de operadores.

Re: Excepto que + es un operador

Cierto, quizás no aplica para Java. Esto lo podríamos ilustrar puramente cómo objetos en Ruby, Scala; en donde todo es un objeto. Sin embargo es una manera sencilla de explicarlo.

style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-5164839828746352"
data-ad-slot="7563230308">