Polimorfismo en Java (Parte I), con ejemplos


El proyecto de este post lo puedes descargar pulsando AQUI.

El Polimorfismo es uno de los 4 pilares de la programación orientada a objetos (POO) junto con la AbstracciónEncapsulación y Herencia. Para entender que es el polimorfismo es muy importante que tengáis bastante claro el concepto de la Herencia, por tanto recomendamos que veáis la entrada en la que hablamos de la Herencia: Herencia en Java, con ejemplos.

Para empezar con esta entrada, se ha de decir que el término "Polimorfismo" es una palabra de origen griego que significa "muchas formas". Este termino se utiliza en la POO para "referirse a la propiedad por la que es posible enviar mensajes sintácticamente iguales a objetos de tipos distintos". Como esta definición quizás sea algo difícil de entender, vamos a explicarla con el ejemplo que pusimos en la entrada de la herencia en la que queríamos simular el comportamiento que tendrían los diferentes integrantes de la selección española de fútbol; tanto los Futbolistas como el cuerpo técnico (Entrenadores, Masajistas, etc…). Para este ejemplo nos vamos a basar en el siguiente diagrama de clases:

PolimorfismoFutbol-diag

NOTA: en este diagrama y en adelante no vamos a poner los constructores y métodos getter y setter con el fin de que el diagrama nos quede grande e “intendible” aunque en un buen diagrama de clases deberían aparecer para respetar el principio de encapsulación de la POO

 

En este ejemplo vamos a tener una clase padre (SelecciónFutbol) en la que tendremos los atributos y métodos comunes a todos los integrantes que forman la selección española de fútbol (Futbolistas, Entrenadores, Masajistas, etc.) y en ella se van a implementar los métodos del comportamiento "genérico" que deben de tener todos los integrantes de la selección. Como ya dijimos en la entrada de la herencia, la herencia no es más que sacar "factor común" del código que escribimos, así que los atributos y métodos de la clase SeleccionFutbol los tendrán también los objetos de las clases Futbolista, Entrenador y Masajista. Antes de seguir vamos a mostrar el código de la clase "SeleccionFutbol" para ver algunas peculiaridades:

public abstract class SeleccionFutbol {

	protected int id;
	protected String nombre;
	protected String apellidos;
	protected int edad;

	// constructores, getter y setter

	public void viajar() {
	     System.out.println("Viajar (Clase Padre)");
	}

	public void concentrarse() {
	     System.out.println("Concentrarse (Clase Padre)");
	}

	// IMPORTANTE -> METODO ABSTRACTO => no se implementa en la clase abstracta pero si en la clases hijas
	public abstract void entrenamiento();

	public void partidoFutbol() {
	     System.out.println("Asiste al Partido de Fútbol (Clase Padre)");
	}
}

Lo primero que nos debe de llamar la atención al ver este código es que utilizamos dos veces la palabra reservada "abstract". Esta palabra nos indica que la clase "SeleccionFutbol" es una clase abstracta y las clases abstractas no se pueden instanciar, por tanto nunca podremos hacer un "new SeleccionFutbol()". Otra cosa que vemos es que también utilizamos la palabra reservada abstract en un método (en el método entrenamiento). Esto quiere decir que todas las clases hijas de la clase "SeleccionFubol" tienen que tener implementado ese método obligatoriamente. Por tanto con esto que se acaba de contar y diciendo que la palabra "Polimorfismo" significa "muchas formas", podéis deducir que la clase "SeleccionFutbol" es una clase que puede adoptar diferentes formas y en este ejemplo puede adoptar las formas de "Futbolista", "Entrenador" y "Masajista".

Ejm_polimorfismo_jarroba

Como vemos un "Entrenador", un "Futbolista" y un "Masajista" pertenecen a la misma clase padre y por eso se instancian diciendo que es una SeleccionFutbol y son nuevos objetos de las clases hijas. Por otro lado vemos que no se pueden crear objetos de una clase abstracta, por tanto el crearnos el objeto "casillas" nos da un error.

Y ahora si hemos dicho que hemos definido en la clase padre un método abstracto que es obligatorio implementar en las clases hijas ¿Como lo hacemos?. Bueno vamos por partes. Una cosa muy buena que tiene la herencia y el polimorfismo, es que las clases hijas no solo heredan los métodos (o la implementación de los métodos) de las clases padre, sino que las clases hijas se pueden especializar.  Esto significa que una clase hija puede "redefinir" los métodos de su clase padre; es decir, que se puede volver a escribir ese método y de ahi la especialización. Para ello vamos a ver la implementación de las clases hijas:

public class Futbolista extends SeleccionFutbol {

   private int dorsal;
   private String demarcacion;

   // constructor, getter y setter

   @Override
   public void entrenamiento() {
      System.out.println("Realiza un entrenamiento (Clase Futbolista)");
   }

   @Override
   public void partidoFutbol() {
      System.out.println("Juega un Partido (Clase Futbolista)");
   }

   public void entrevista() {
      System.out.println("Da una Entrevista");
   }
}
public class Entrenador extends SeleccionFutbol {

   private int idFederacion;

   // constructor, getter y setter
	
   @Override
   public void entrenamiento() {
      System.out.println("Dirige un entrenamiento (Clase Entrenador)");
   }

   @Override
   public void partidoFutbol() {
      System.out.println("Dirige un Partido (Clase Entrenador)");
   }

   public void planificarEntrenamiento() {
      System.out.println("Planificar un Entrenamiento");
   }
}
public class Masajista extends SeleccionFutbol {

   private String titulacion;
   private int aniosExperiencia;

   // constructor, getter y setter
	
   @Override
   public void entrenamiento() {
      System.out.println("Da asistencia en el entrenamiento (Clase Masajista)");
   }

   public void darMasaje() {
      System.out.println("Da un Masaje");
   }
}

Como vemos en el código todas las clases hijas tienen implementada el método "entrenamiento()" ya que como dijimos al tenerlo en la clase padre como método abstracto, es obligatorio que todas las clases hijas tengan ese método. Por otro lado observamos en el código que encima del método "entrenamiento()" y otros métodos, tenemos la etiqueta "@Override". Esta etiqueta sirve para indicar en el código que estamos "re-escribiendo o especializando" un método que se encuentra en la clase padre y que queremos redefinir en la clase hija. Si os fijáis esta etiqueta solo y exclusivamente esta en los métodos de las clases hijas que tenemos definida en la clase padre, por tanto cuando se llame a esos métodos, las clases hijas ejecutaran el método redefinido en la clase hija y las que no lo hayan redefinido se ejecutará es método de la clase padre. En la siguiente imagen vemos como hacemos estas especializaciones:

Polimorfismo_especializacion_jarroba

Con todo esto ya podemos empezar a ejecutar el programa que simulará el comportamiento de los integrantes de la selección española y ver las diferentes formas que adoptan cada uno de los integrantes de la selección. Para ello empecemos mostrando el siguiente fragmento de código:

public class Main {

	// ArrayList de objetos SeleccionFutbol. Idenpendientemente de la clase hija a la que pertenezca el objeto
	public static ArrayList<SeleccionFutbol> integrantes = new ArrayList<SeleccionFutbol>();

	public static void main(String[] args) {
		
		SeleccionFutbol delBosque = new Entrenador(1, "Vicente", "Del Bosque", 60, 28489);
		SeleccionFutbol iniesta = new Futbolista(2, "Andres", "Iniesta", 29, 6, "Interior Derecho");
		SeleccionFutbol raulMartinez = new Masajista(3, "Raúl", "Martinez", 41, "Licenciado en Fisioterapia", 18);

		integrantes.add(delBosque);
		integrantes.add(iniesta);
		integrantes.add(raulMartinez);

		// CONCENTRACION
		System.out.println("Todos los integrantes comienzan una concentracion. (Todos ejecutan el mismo método)");
		for (SeleccionFutbol integrante : integrantes) {
			System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> ");
			integrante.concentrarse();
		}

		// VIAJE
		System.out.println("nTodos los integrantes viajan para jugar un partido. (Todos ejecutan el mismo método)");
		for (SeleccionFutbol integrante : integrantes) {
			System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> ");
			integrante.viajar();
		}

      .........
}

Como vemos nos hemos creado tres objetos de la clase SeleccionFutbol que adoptan una de las tres formas que pueden adaptar (Entrenador, Futbolista y Masajista)  y los metemos en un “ArrayList” de objetos de la clase “SeleccionFutbol”. Ahora al ejecutar este fragmento de código vamos a ver que todos tienen el mismo comportamiento a la hora de "concentrarse()" y "viajar()", por tanto ejecutarán el método de la clase padre:

Todos los integrantes comienzan una concentracion. (Todos ejecutan el mismo método)
Vicente Del Bosque -> Concentrarse (Clase Padre)
Andres Iniesta -> Concentrarse (Clase Padre)
Raúl Martinez -> Concentrarse (Clase Padre)

Todos los integrantes viajan para jugar un partido. (Todos ejecutan el mismo método)
Vicente Del Bosque -> Viajar (Clase Padre)
Andres Iniesta -> Viajar (Clase Padre)
Raúl Martinez -> Viajar (Clase Padre)

Hasta el momento nada nuevo y sorprendente, pero ahora vamos a ver como cada uno de los integrante al lanzarse los mismos métodos ("entrenamiento()" y "partidoFutbol()") tienen un comportamiento diferente:

     ........
// ENTRENAMIENTO
System.out.println("nEntrenamiento: Todos los integrantes tienen su función en un entrenamiento (Especialización)");
for (SeleccionFutbol integrante : integrantes) {
	System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> ");
	integrante.entrenamiento();
}

// PARTIDO DE FUTBOL
System.out.println("nPartido de Fútbol: Todos los integrantes tienen su función en un partido (Especialización)");
for (SeleccionFutbol integrante : integrantes) {
	System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> ");
	integrante.partidoFutbol();
}
     ........

Vemos el resultado al ejecutar este fragmento de código:

Entrenamiento: Todos los integrantes tienen su función en un entrenamiento (Especialización)
Vicente Del Bosque -> Dirige un entrenamiento (Clase Entrenador)
Andres Iniesta -> Realiza un entrenamiento (Clase Futbolista)
Raúl Martinez -> Da asistencia en el entrenamiento (Clase Masajista)

Partido de Fútbol: Todos los integrantes tienen su función en un partido (Especialización)
Vicente Del Bosque -> Dirige un Partido (Clase Entrenador)
Andres Iniesta -> Juega un Partido (Clase Futbolista)
Raúl Martinez -> Asiste al Partido de Fútbol (Clase Padre)

En este caso vemos que todos los integrantes ejecutan el método "entrenamiento()" de forma diferente ya que al ser este método abstracto en la clase padre, les forzamos a las clases hijas a que implementen ese método. Por el contrario al ejecutar el método "partidoFutbol()" vemos que el objeto de la clase Masajista utiliza el método implementado en la clase padre y en cambio los objetos de la clase Futbolista y Entrenador ejecutan sus método "re-implementados o especializados" que se volvieron a escribir en sus clases.

Por último vamos a ver que cada uno de los objetos puede ejecutar métodos propios que solamente ellos los tienen como son el caso de "planificarEntrenamiento(), entrevista() y  darMasaje()" que solo los pueden ejecutar objetos de la clase Entrenador, Futbolista y Masajista respectivamente:

     ........
// PLANIFICAR ENTRENAMIENTO
System.out.println("nPlanificar Entrenamiento: Solo el entrenador tiene el método para planificar un entrenamiento:");
System.out.print(delBosque.getNombre() + " " + delBosque.getApellidos() + " -> ");
((Entrenador) delBosque).planificarEntrenamiento();

// ENTREVISTA
System.out.println("nEntrevista: Solo el futbolista tiene el método para dar una entrevista:");
System.out.print(iniesta.getNombre() + " " + iniesta.getApellidos() + " -> ");
((Futbolista) iniesta).entrevista();

// MASAJE
System.out.println("nMasaje: Solo el masajista tiene el método para dar un masaje:");
System.out.print(raulMartinez.getNombre() + " " + raulMartinez.getApellidos() + " -> ");
((Masajista) raulMartinez).darMasaje();
     ........

Como resultado de la ejecución de este fragmento de código tenemos lo siguiente:

Planificar Entrenamiento: Solo el entrenador tiene el método para planificar un entrenamiento:
Vicente Del Bosque -> Planificar un Entrenamiento

Entrevista: Solo el futbolista tiene el método para dar una entrevista:
Andres Iniesta -> Da una Entrevista

Masaje: Solo el masajista tiene el método para dar un masaje:
Raúl Martinez -> Da un Masaje

CONCLUSIONES Y ACLARACIONES:

Como hemos visto el polimorfismo es un concepto un poco más avanzado que la herencia y puede ser muy util a la hora de jerarquizar y querer dar un patrón de comportamiento común a una serie de objetos que heredan de la misma clase. En esta entrada no hemos visto todo lo referente al polimorfismo ya que nos quedaría ver un concepto un poco más avanzado en Java (y en otros lenguajes también) como son las "Interface" (clases abstractas puras) de las cuales hablaremos en otra entrada para terminar de ver lo que es el polimorfismo.

Por último es muy probable para los que estéis empezando con la POO que no veáis mucho sentido a esto del polimorfismo y al principio es normal. Solo os debo de decir que a base de experiencia se le encuentra sentido al polimorfismo, por tanto si teneis que hacer alguna práctica en la universidad o lo que sea en la que tengais que usar el polimorfismo intentar entenderlo y hacer lo que os pidan porque entenderlo 100% es complicado y se requiere de experiencia para ello.

Comparte esta entrada en:
Safe Creative #1401310112503
Polimorfismo en Java (Parte I), con ejemplos por "www.jarroba.com" esta bajo una licencia Creative Commons
Reconocimiento-NoComercial-CompartirIgual 3.0 Unported License.
Creado a partir de la obra en www.jarroba.com

98 thoughts on “Polimorfismo en Java (Parte I), con ejemplos”

  1. Hola buenas, en la clase main..por que se usa un arrayList estático??…Se podría haber declarado el array dentro del main igualmente no??
    Espero respuesta.Gracias.

    1. Buenas Ivan.

      Es estático porque main lo es.

      Sí, se podía haber declarado dentro del main si no se va a usar en otra función aparte; aquí se puso fuera simplemente por poder utilizarlo en otras funciones si se deseara. Puedes ponerlo dentro del main si quieres.

  2. Muchas gracias por tu trabajo.
    Un articulo del 2014 que sigue visitado y atendido con respuestas en el 2020, Felicidades.

    1. Gracias German, me alegro que te guste.
      Aunque sea un artículo de hace unos años, mientras podamos daremos respuesta a las preguntas 😉

      1. Bro sinnmentir es lo mejor que eh leido explicas demasiado bien son las 2 de la mañan en colombia y me eh leido 4 articulo de los que has publicado y es lo mejor que eh visto hasta hora puede ser viejonperp es sustancial

  3. buen dia me podrían apoyar con las siguiente dudas por favor:

    1. Si escribo iniesta. solo me aparecen los métodos abstracto y el método sobre escrito (concentrarse, entrenamiento, ademas de los métodos de la clase seleccion..) que pasa con los demás métodos de la clase futbolista?

    2. porque debo de usar ((Futbolista) iniesta).entrevista(); para llamar entrevista y no puedo hacer esto Futbolista.iniesta.enrevista(); o bien iniesta.entrevista(); o integrante.iniesta.entrevista();

    Saludos.

    1. Buenas Christian.

      1 Los demás métodos de la clase futbolista los puedes utilizar directamente, pues ya están escritos (tienen código dentro). Por ejemplo, utilizarlo desde la clase hija:
      this.concentrarse()
      2 Se hace así para indicar que vas a usar el objeto «iniesta» que es de tipo SeleccionFutbol pero quieres utilizar el método concretamente de tipo Futbolista (que está en esta clase). El resto que indicas no los he probado pero no me parecen sintaxis de Java para lo que se quiere hacer (si quieres utilizar un método estático sí se podría utilizar como indicas, pero no para la herencia)

  4. Grácias por la explicación. Ahora ya entiendo mucho mejor lo del polimorfismo. De todas maneras si una clase hija no ha de implementar un método de la clase padre, hay alguna manera de proteger este comportamiento ???
    Por ejemplo, que la clase «masajista» no pudiese implementar el método «calentamiento» que pudiese estar en la clase padre.

    1. Hola Agustí.

      Poniendo como private el método de la clase padre la hija no lo podría utilizar.

  5. tengo una duda que justamente viene a cuento de esto… si yo implemento exactamente el mismo programa, con el mismo array dinámico de la clase padre, y en algún momento, por alguna razón yo quiero obtener el dorsal de un jugador o la titulación del masajista, no puedo porque la herencia solo sirve para obtener los métodos de la clase padre.. entiendo eso… pero es tan lindo hacer un array de la clase padre que tiene que haber una forma de obtener el getDorsal de otra forma que no sea instanciando la clase hija futbolistas….

    1. Con una herencia podrías utilizar tanto las clases públicas de la clase padre como las propias de la clase hija, entiendo que para que uses el dorsal de un futbolista tenes que hacer lo siguiente: ((Futbolista) Iniesta ).getDorsal(); y no es necesario crear una nueva instancia de la clase futbolista (no sé si a eso te referías).

  6. Hola que tal! me ayudo muchísimo tu explicación, tengo un ejercicio muy similar que intento realizar pero hay algo que no se como hacerlo, me ayudas porfavor?:
    .
    //Ya tengo implementados mis métodos correctamente (Padre llamada «Persona» abstract, e hijas «Futbolsita», «Entrenador», «Doctor» con @Override).
    .
    ¿Como puedo en mi array de objetos polimórifcos (guarde ahi a los futbolistas, entrenadores y doctores), solo mandar llamar a métodos
    propios que solamente ellos los tienen «curar» o «Planificar entrenamiento., ya que no se cuantos futbolistas ha ingresado ni entrenadores, ni doctores, y ni en que posición…mira:
    .
    //Creo mi arreglo de objetos:
    static ArrayList persona = new ArrayList();

    //fragmento de como capturo a un futbolista, es lo mismo para doctor y entrenador:
    static public void crearFutbolista() {
    String nombre, apellido, posicion;
    int edad, dorsal;

    System.out.println(«Creando un futbolista:»);
    System.out.print(«Ingresa nombre: «);
    scanner.nextLine();
    nombre = scanner.nextLine();
    System.out.print(«Ingresa apellido: «);
    apellido = scanner.nextLine();
    System.out.print(«Ingresa edad: «);
    edad = scanner.nextInt();
    System.out.print(«Ingresa dorsal: «);
    dorsal = scanner.nextInt();
    System.out.print(«Ingresa posición: «);
    scanner.nextLine();
    posicion = scanner.nextLine();

    Futbolista futbolista = new Futbolista(nombre, apellido, edad, dorsal, posicion);
    //Ingreso a la persona futbolista en el arreglo persona.
    persona.add(futbolista);
    }

    //PARA TODOS LO QUE SON MÉTODOS ABSTRACTOS me funciona bien ésto:
    //Al momento de imprimir una acción:
    static public void viaje() {
    for (Persona per : persona) {
    System.out.println(«Soy » + per.getNombre() + » » + per.getApellido());
    per.viaja();
    System.out.println(«»);
    }
    }

    //PERO CUANDO INTENTO HACERLO CON LOS MÉTODOS PROPIOS ESTA MAL:
    static public void curar() {
    for (Persona per : persona) {
    System.out.println(«Soy » + ((Doctor) per).getNombre() + » » + ((Entrenador) per).getApellido());
    ((Doctor) per).cura();
    System.out.println(«»);
    }
    }

    //No se como recorrerlo, me puedes ayudar porfavor, te lo agradecería mucho.

    1. Si el listado es del tipo de la clase padre (por ejemplo «SeleccionFutbol») podrás añadirle cualquier objeto de sus clases hijas (como Futbolista, Entrenador, Masajista), por lo que puedes llamar al método que quieras invocar de la clase padre (por ejemplo «viajar()»), ya que al heredarlo sus hijos todos lo tienen el mismo método.

      for (SeleccionFutbol integrante : integrantes) {
      integrante.viajar();
      }

      Si lo que quieres es llamar a algún método en concreto de cada clase hija, tendrás que crear condiciones para preguntar por el tipo de la clase en concreto (con «instanceOf») y ejecutar para cada uno su método en concreto dentro de cada parte de la condición. He de advertir que usar «instanceOf» no es lo mejor, ni lo más recomendado, y es algo que hay que evitar; además que seguramente si estás dando clase (en academia o universidad) lo tendrás prohibido utilizar (pista: posiblemente tendrás que crear un método en la clase padre).
      for (SeleccionFutbol integrante : integrantes) {
      if (integrante instanceof Entrenador){
      integrante.planificarEntrenamiento();
      }else if (integrante instanceof Futbolista){
      integrante.entrevista();
      }
      }

  7. muy buen post, me salio y me quedo mucho mas claro lo de lo polimorfismo, me gustaria que explicaras lo de interfaces esta muy completo gracias amigo

  8. Es un poco complicado entender todo esto, y es justo lo que me pasa , pero, debo decir que gracias a estos contenidos.
    he aprendido a entender mucho mejor todos estos conceptos
    ¡Gracias muchas gracias.

  9. Llevo buscando algún vídeo o vlog que me explique qué son la herencia y el polimorfismo todo el día. Solo puedo decir GRACIAS GRACIAS GRACIAS… UN MILLÓN DE GRACIAS. Un saludo enorme.

    1. Nos alegra que te haya sido útil el artículo Andrés.
      Muchos ánimos con la programación 🙂

  10. buenas tardes amigo muy interesante el codigo y todas las opiniuones expresadas en este foro sin embargo tengo un problemita me ecargue de realizar el cuerpo del programa tal cual esta aqui y me arroja como resultado lo siquiente :
    Todos los integrantes comienzan una concentracion. (Todos ejecutan el mismo método)
    nullnull -> Concentrarse (Clase Padre)
    nullnull -> Concentrarse (Clase Padre)
    nullnull -> Concentrarse (Clase Padre)
    Todos los integrantes viajan para jugar un partido. (Todos ejecutan el mismo método)
    null null -> Viajar (Clase Padre)
    null null -> Viajar (Clase Padre)
    null null -> Viajar (Clase Padre)
    Entrenamiento: Todos los integrantes tienen su función en un entrenamiento (Especialización)
    null null -> Dirige un entrenamiento (Clase Entrenador)
    null null -> Realiza un entrenamiento (Clase Futbolista)
    null null -> Da asistencia en el entrenamiento (Clase Masajista)
    Partido de Fútbol: Todos los integrantes tienen su función en un partido (Especialización)
    null null -> Dirige un Partido (Clase Entrenador)
    null null -> Juega un Partido (Clase Futbolista)
    null null -> Asiste al Partido de Fútbol (Clase Padre)
    Planificar Entrenamiento: Solo el entrenador tiene el método para planificar un entrenamiento:
    null null -> Planificar un Entrenamiento
    Entrevista: Solo el futbolista tiene el método para dar una entrevista:
    null null -> Da una Entrevista
    Masaje: Solo el masajista tiene el método para dar un masaje:
    null null -> Da un Masaje

    por que me muestra null en vez de los nombres de los jugadores o los que ya estan preestablecidos en el programas mediantes los getters y setters no se si me expolico de igual forma descargue el proyecto desde la pagina de PolimorfismoJarroba-master y me sigue arrojando el mismo error no comprendo la falla soy nuevo en el lenguaje pido disculpas por mi ignorancia

    1. Hola como estas, si tienes los fuentes como se muestra en el ejemplo, lo más seguro es que debas agregar los respectivos métodos constructores en cada una de las clases (Futbolista, Entrenador y Masajista)

      Ejemplo

      public class Entrenador{

      private int idFederacion;

      public Entrenador(int idFederacion, int id, String nombre,
      String apellidos, int edad ){
      //Debe existir en constructor de la clase abstract para que super funcione e inicialice
      //todos los atributos de las clases IMPORTANTE
      super(id, nombre, apellidos, edad);

      this.idFederacion = idFederacion;

      }

      }

      comenta si te funcionó por favor.

    2. Puedes revisar que a la hora de guardar los valores se está guardando null o bien en la clase SeleccionFutbol los métodos getNombre y getApellidos están devolviendo null.

  11. En el ejemplo se esta incumpliendo el principio de Liskov, ya que las clase hijas no se pueden cambiar por la clase hija ya que tiene comportamientos que la clase padre no tiene y eso viola el principio.

    1. Estoy revisando el artículo y no veo donde dices que viola el principio de sustitución de Liskov (Si eres tan amable de indicarme dónde lo corregiremos). Explico que lo cumple:

      Tenemos las clases P (padre con métodos A) y H (hija con métodos B, que hereda de P) tal que:
      P = {A}
      H = {P, B} = {{A}, B}
      El principio de sustitución de Liskov nos dice que:
      P = H

      De ahí que si tenemos SeleccionFutbol como padre (P) e interfaz (métodos abstractos), construyamos Futbolista, Entrenador y Masajista como hijos (H), los cuales se pueden instanciar como (P = H):
      SeleccionFutbol delBosque = new Entrenador(…);
      SeleccionFutbol iniesta = new Futbolista(…);
      SeleccionFutbol raulMartinez = new Masajista(…);

    2. El principio de Liskov se establece a nivel del comportamiento de cada uno de los métodos de la clase padre e hija(s), es decir, el comportamiento de un método heredado por una clase hija, debe ser igual al comportamiento de dicho método en la clase padre.
      Al definir el método «partidoFutbol» en la clase padre y luego «cambiar su comportamiento» en las clases hijas, violas el principio de Liskov.

  12. Alguien sabe como se imprime desde el main los valores de las variables del masajista, que pasan al metodo SUPER?

    private String titulacion;
    private int aniosExperiencia;

    1. 1.- Una vez que tienes declarados tus metodos set y get pertenecientes a cada campo PRIVATE.
      2.- Dentro del iterados realizar una conversión descendente es tiempo de ejecusion en base a la creacion de un objeto, esto debido a que Masajista ES UN ‘SeleccionFutbol’ entonces esos dos campos no existen
      en la clase padre:
      SeleccionFutbol integrante= ((SeleccionFutbol)integranteActualIteracion);
      3.-De esta manera ya puedes utilizar los metodos get y set para obtener o colocar un valor en ‘Masajista’ asi:
      integrante.getTitulacion();
      integrante.getExperiencia();

    2. Puedes poner getter o métodos con el System.out.println en el padre (super. Como lo está el método darMasaje() ) para llamar desde el hijo directamente (desde el Main por ejemplo).

  13. …felicitaciones a todos…y gracias por los aportes…son sustanciales para el desarrollo del conocimieno…incluso los mas «tontos» que hoy no he publicado….»jarroba.com» es un Maestro. Ëxitos!!

  14. Hola,

    Creo que el ejemplo mostrado funcionaria EXACTAMENTE igual si usamos la sintaxis para declarar una referencia con herencia normal:

    Entrenador delBosque = new Entrenador(..);
    Futbolista iniesta = new Futbolista(…);
    Masajista raulMartinez = new Masajista(3, …);

    Si esto es correcto… ¿Cual es la ventaja de declararla en forma polimórfica?

    SeleccionFutbol delBosque = new Entrenador(…);
    SeleccionFutbol iniesta = new Futbolista(…);
    SeleccionFutbol raulMartinez = new Masajista(…);

    ¿Que cosas me permite hacer una referencia declarada en forma polimórfica, que no podría hacer si la declaro en forma (con herencia) normal?

    Saludos y Gracias!

  15. Muchas gracias por compartir, sin embargo me surge la duda, por que para llamar el metodo de por ejemplo darMasaje se escribe asi ((Masajista) raulMartinez).darMasaje();?, no podría ser raulMartinez.darMasaje()??. Saludos!

    1. Porque el objeto ‘raulMartinez’ es una variable de tipo ‘SeleccionFuntbol’ con referencia a la clase ‘Masajista’, esto al momento de inicializarlo, entonces hay que hacer una conversion descendente para acceder a sus metodos y atributos especificos.

  16. Hola!
    Primero que nada, felicidades!
    Tu website en verdad es una maravilla y me ha ayudado muchísimo a comprender mucho mejor lo que veo en clases.
    Quería preguntarte si de casualidad tienes el código completo de este ejemplo?
    En ocasionas aún me sigo perdiendo en el acomodo de todo el programa, ya que en mis clases mi maestro nos lo plantea en dos distintos tabs, donde está el main y luego donde llamamos a todos los métodos.
    Muchas gracias!

  17. Estimado me surge la siguiente duda:
    En el código, si en vez de «SeleccionFutbol» pusiera:
    Entrenador delBosque = new Entrenador(1, «Vicente», «Del Bosque», 60, 28489);
    Futbolista iniesta = new Futbolista(2, «Andres», «Iniesta», 29, 6, «Interior Derecho»);
    Masajista raulMartinez = new Masajista(3, «Raúl», «Martinez», 41, «Licenciado en Fisioterapia», 18);

    Aún así funcionaría para todo el ejemplo?
    Porque de ser así solo estaría ocupando herencia y funcionaría igual o no?
    Ojala se entienda mi duda.
    Saludos!

    1. Funcionaría igual, pero estarías escribiendo un código «no muy correcto». Es preferible poner su herencia por si en un futuro quisieras crear otro «Entrenador_diferente» por ejemplo, entonces el código entero estaría limitado a «Entrenador»

      Entrenador delBosque = new Entrenador(1, «Vicente», «Del Bosque», 60, 28489); // Está limitado a instanciar una clase Entrenador

      Entrenador delBosqueMejorado = new Entrenador_diferente(1, «Vicente», «Del Bosque», 60, 28489); // No es posible poner porque el tipo es Entrenador

      Y te tocaría reescribirlo todo otra vez (al menos las partes dependientes). Si utilizas la herencia «SeleccionFutbol», y mañana creas otra clase que herede de ésta, tu código funcionará con cambiar la instancia:

      SeleccionFutbol delBosque = new Entrenador(1, «Vicente», «Del Bosque», 60, 28489);

      SeleccionFutbol delBosqueMejorado = new Entrenador_diferente(1, «Vicente», «Del Bosque», 60, 28489);

  18. Suena un poco trivial pero me interesa saber con que herramienta realizaste tus diagramas de clases.

    Saludos

  19. El mejor sitio web que he encontrado. el contenido que tiene es preciso y cuenta con ejemplos concretos; aqui uno aprende hasta para que sirve la ultima coma que uno escribe en codigo.

  20. El mejor sitio web que he encontrado, nada comparado con otros. Felicitaciones por todo el contenido que nos permite compartir; con ejemplos claros y describiendo cada cosa; así uno entiendo hasta para que sirve la ultima coma del codigo que uno escribe.

  21. Aun me queda la duda a la hora de crear los objetos.

    En este ejemplo se hizo de la siguiente manera:

    SeleccionFutbol delBosque = new Entrenador(1, "Vicente", "Del Bosque", 60, 28489);

    No seria de esta manera:

    Entrenador delBosque = new Entrenador(1, "Vicente", "Del Bosque", 60, 28489);

    Saludos. 🙂

    1. Sí sería de la manera tradicional y funcionaría hacer esto:

      Entrenador algoDeLaSeleccionDeFutbol = new Entrenador(…);

       

      Pero NO funcionaría si queremos hacer esto:

      Entrenador algoDeLaSeleccionDeFutbol = new Futbolista(…);

       

      Pero en este artículo queríamos dejar clara el uso del polimorfismo, aunque tu duda es de Herencia (No confundamos Herencia con Polimorfismo).

      En respuesta a la pregunta sobre Herencia. Imagina si queres hacer un método que permite cualquiera de las tres clases porque es capaz de utilizarlas, como:

      public static void miFuncion(SeleccionFutbol algoDeLaSeleccionDeFutbol ) {

          algoDeLaSeleccionDeFutbol.partidoFutbol() //El método partidoFutbol() existe tanto en Entrenador, Futbolista y Masajista

      }

      Por lo que, le podríamos pasar cualquiera de estas variantes:

      SeleccionFutbol algoDeLaSeleccionDeFutbol = new Entrenador(…);

      SeleccionFutbol algoDeLaSeleccionDeFutbol = new Futbolista(…);

      SeleccionFutbol algoDeLaSeleccionDeFutbol = new Masajista(…);

       

      Un ejemplo muy útil del polimorfismo es para List. En vez de definir:

      ArrayList list = new ArrayList();

      Es de mejor programador definir:

      List list = new ArrayList();

      Pues de esta forma, en un futuro podríamos cambiar de List de una forma muy sencilla, o incluso mejorar nosotros la clase List.

      Por ejemplo, podríamos utilizar la clase Stack que hereda de List (Tiene todas las implementaciones de List en https://docs.oracle.com/javase/7/docs/api/java/util/List.html donde pone «All Known Implementing Classes»):

      List list = new Stack();

    1. Es muy sencillo, te lo explicamos aquí, solo tienes que seguir los pasos de este artículo 🙂

  22. Tengo una duda,

    si definimos como abstracta la clase padre y el metodo elegido para luego modificarlo, que sentido tiene especializar el metodo de la clase hija? O sea al finl puedo especializar los metodos que necesito de la clase y listo me evito todo el rollo

     

    ayuda

    1. La gracia de las clases abstractas es obligar a definir ciertas funcionalidades. Un ejemplo que creo que es muy claro. Cuando haces click en un botón de la interfaz algo tiene que ocurrir para ese botón en concreto. Si eres el desarrollador de la interfaz y quieres obligar a otro desarrollador a implementar la funcionalidad dentro de un método en particular (quieres pasarle unos parámetros, por ejemplo las coordenadas del cursor al pulsar el botón; y quieres que el otro desarrollador te devuelva algún dato, por ejemplo si mantener el botón activo o desactivado al retornar en el método un True o un False). El siguiente ejemplo de código es pseudocódigo de Android (que es Java), pero se ve muy bien:

      miBoton.setOnClickListener(new OnClickListener() {

                public void onClick(int coordenadaX, int coordenadaY) {

                          // Realizar algo al pulsar el botón

                          desactivarBoton = True;

                         return desactivarBoton;

                }

      });

      Se puede apreciar que tengo un objeto botón (miBoton) que tiene un método setOnClickListener() que espera el objeto de la clase OnClickListener, clase interfaz (o clase abstracta si queremos implementar algo en los métodos en las clases que la hereden) que tiene el método abstracto onClick(). La clase OnClickListener sería algo así:

      public static interface OnClickListener {

                public abstract boolean onClick(int coordenadaX, int coordenadaY);

      }

  23. Tengo una pregunta.. 

    Debido a que un metodo abstracto debe ser si o si implementado en la clase hija, porque no directamente declarar y definir un metodo en la clase hija y no declararlo en la clase padre, para luego ser reescrito en la clase hija?  No se si me explico je.. 

     

     

    1. Buenas pregunta. Se hace así para poder tneer el mismo método con varias implementaciones. Imagina que queremos utilizar la clase botón que tengamos que implementar el onClick() (lo que se hará al pulsar el botón). Dependiendo de la clase (o método) donde se implemente, el onClick() actuará de una manera o de otra (por ejemplo, al hacer click mostrará un mensaje, incrementará un contador, cambiará un texto, etc)

      1. Buenas noches.
        Primera mente me ha gustado el artículo sobre polimorfismo. Me gustaría que respondieras una pregunta sobre el ejemplo.
        ¿Cómo se accede desde el arraylist integrantes a los atributos privados de los objetos de Entrenador, Masajista y Futbolista que fueron agregados a dicha lista? o ¿se debe crear otro arraylist para ello?, ya que en el ejemplo sólo define un futbolista y si se añadiera otro futbolista debe definirse otro objeto futbolista y añadirlo a integrantes, ¿Cómo se accede a los atributos privados de esos dos futbolista desde el arraylist integrantes ?. De antemano agradezco su apoyo.

        1. Buenas Jonathan.
          Para acceder a los elementos primero obtienes el elemento del arraylist y luego puedes invocar al método que necesites siempre que sea compartido (heredado del padre o abstracto).

          1. Gracias por su respuesta pero puedes indicarme a través de un fragmento de código, ya que los intento y no logro obtener la información.

          2. Como integrantes es un arrayList de SeleccionFutbol puedes hacer como en el propio ejemplo, recorrer los elementos del arrayList con un for y usar cada uno de sus métodos (como getNombre, getApellido, etc.):

            for (SeleccionFutbol integrante : integrantes) {
            			System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> ");
            			integrante.concentrarse();
            		}
          3. Gracias por su ayuda. De pronto no me hice entender cuando formule la pregunta, me disculpo. Eso datos son los comunes y a esos puedo acceder, me refirió a los particulares de las sub-clases o clases derivadas, ¿Cómo accedo a esos datos desde el Arraylist?, puesto que el ejemplo muestra son los atributos comunes.

          4. Si quieres afinar a las clases hijas, ya tendrás que ir preguntando a cada uno si es instancia de tal u otra clase:

            if (hijo instanceof ClaseHijo){
                // Trabajar con el hijo
            }
  24. Hola buen día, creo que el ejercicio y la explicación son bastante útiles; solo quería agregar algo, las clases abstractos con métodos abstractos se usan por el simple hecho de no conocer el cómo debería funcionar la funcion, puede ser por falta de información previa; por esta razón, las clases hijas deberán implementar el método con su respectiva funconalidad.

    1. Gracias Edgar de parte de toda la comunidad de desarrolladores, estoy seguro que más de uno estará muy agradecido 🙂

  25. Citando a lo que mencionaste: "la clase “SeleccionFutbol” es una clase que puede adoptar diferentes formas y en este ejemplo puede adoptar las formas de “Futbolista”, “Entrenador” y “Masajista”."

    SeleccionFutbol casillas=new SeleccionFutbol();
    SeleccionFutbol delBosque=new Entrenador();
    SeleccionFutbol iniesta=new Futbolista();
    SeleccionFutbol raulMartinez=new Masajista();

    Realmente no es la clase la que adopta las diferentes formas sino la referencia a objeto de la clase SeleccionFutbol que son en el ejemplo las variables casillas,delBosque,iniesta y raulMartinez. Para ver más claramente como esas referencias cambian de forma les dejo un ejemplo.

    1     SeleccionFutbol sf;          //sf es una variable del tipo SeleccionFutbol
    2     sf=new Entrenador();     // sf es una referencia a un objeto Entrenador
    3     sf.entrenamiento();       // imprime "Dirige un entrenamiento (Clase Entrenador)"
    4
    5     sf=new Futbolista();     // sf es una referencia a un objeto Futbolista
    6     sf.entrenamiento();      // imprime "Realiza un entrenamiento (Clase Futbolista)"
    7
    8     sf=new Masajista();     // sf es una referencia a un objeto Masajista
    9     sf.entrenamiento();      // imprime"Da asistencia en el entrenamiento (Clase Masajista)"

    Como se puede ver, la variable o referencia "sf" cambia de forma en las lineas 2, 5 y 8, es decir, la referencia a objeto "sf" es polimorfica. Como consecuencia del polimorfismo la misma linea de codigo "sf.entrenamiento();" que están en las lineas 3, 6 y 9 se comportan de distinta manera imprimiendo resultados diferentes.

    Fuente 1: Libro Programador Certificado Java 2 Curso Practico 2da Edicion (Muy buen libro, se los recomiendo)
    Fuente 2 :http://profesores.fi-b.unam.mx/carlos/java/java_basico3_5.html     &nbsp;
     

  26. Muy bueno la explicación de los conceptos, lo único que no me gusta eso de:  interfaz = clase abstractas puras, las interfaces son interfaces, las clases abstractas son clases abstractas, estas últimas se heredan, las interface se implementan.

    saludos.

    1. Se entiende mejor de esa manera, si dices una intefaz es una interfaz es un poco vacia la explicacion porque no tienes con que asociarla. Por lo mismo se entiende de mejor manera que lo asocie con una clase abstracta ya que aplican las mismas ideas como tener metodos sin implementar. Para un novato será dificil entender que es una interfaz y se podria decir que esta es una clase asbtracta mas avanzada. En el camino se entendera que una interfaz se implementa, ya que explicitamente tienes que indicarlo en la clase con la palabra clave "implements".

    2. Pero los métodos de una clase abstracta también se IMPLEMENTAN en un clase hija así que puede tomar una parte del funcionamiento de una interfaz, ¡saludos!

  27. Hola, muchas gracias le entendí perfectamente al concepto, en lo que tengo duda es:
    -¿Porqué accedes a los métodos propios de las clases hijas de esta manera:

    ((Masajista) raulMartinez).darMasaje();

    y en el ejemplo de Herencia lo haces de esta manera?:

    delBosque.dirigirPartido();

    -¿Tiene algo que ver a la hora de instaciar el método de esta manera:

    SeleccionFutbol delBosque = new Entrenador(1, «Vicente», «Del Bosque», 60, 28489);

    En vez de esta?:

    Entrenador delBosque = new Entrenador(1, «Vicente», «Del Bosque», 60, «284EZ89»);

    -Otra pregunta, ¿Cuál es la diferencia de crear objetos de esas respectivas maneras, si de todos modos en las clases hijas están sobreescritos esos métodos?¿Es debido a que algunas clases hijas no sobreescriben los métodos?

    Gracias, Espero una respuesta 🙂

        1. -¿Porqué accedes a los métodos propios de las clases hijas de esta manera:

          ((Masajista) raulMartinez).darMasaje();

          Sino hace eso solo ve los metodos de la clase SeleccioFutbol y esa clase no tiene el metodo darMasaje()

          Ahí lo que esta haciendo es castear la clase a masajista y asi obtener todos los metodos de la clase SeleccionFubtol maslas de masajista

    1. Respuesta a pregunta 1: porque se declaro una variable de tipo ‘SeleccionFutbol’ en este caso en tiempo de ejecucion Masajista es un ‘SeleccionFutbol’ y ocupas transformarlo o convetirlo para acceder a sus metodos y atributos.

  28. Quiero realmente felicitarlos por la forma en la que llevan adelante las explicaciones, son GENIALES! En muchos lugares, foros y blogs se arman unos enrriedos barbaros para intentar explicar Polimorfismo, ustedes lo hicieron fabuloso!

    Un saludo muy grande desde Roldan, Santa Fe, Argentina!

  29. Los metodos Propios de cada clase como el de planificarEntrenamiento() o darMasaje() no me aparecen en la lista de metodos despues de «delBosque.» y me marca error si lo pongo y me pide que agrege el metodo en la clase SeleccionFutbol Ayuda porfavor. Se los agradeceria mucho. Los demas metodos si me funcionaron, solo los que son unicos de cada clase hija no.

    1. Hola Jerry.
      Tal y como me lo comentas nose exactamente que error te puede dar ya que no entiendo muy bien tu pregunta. Solo decirte que los métodos de planificarEntrenamiento() y darMasaje() son métodos propios de las clases Entrenador y Masajista por tanto no te puede aparecer que agreges métodos de la clase SelecciónFurbol. Especificanos un poco más para ver que errores te da aquí en la entrada o en nuestro foro (http://jarroba.com/foro/)
      SL2

      1. El error que le aparece a jerryes sencillo, solamente tienes que ubicar en cada clase hija el orden de sus atributos ya que estan acomodados algunos en desorden.

        ejemplo:

        como aparece:

           public Futbolista(int dorsal, String demarcacion, int id, String nombre, String apellidos, int edad) {
                super(id, nombre, apellidos, edad);
                this.dorsal = dorsal;
                this.demarcacion = demarcacion;
            }

        Como en realidad es :

         public Futbolista(int id, String nombre, String apellidos, int edad,int dorsal,String demarcacion) {
                super(id, nombre, apellidos, edad);
                this.dorsal = dorsal;
                this.demarcacion = demarcacion;
            }
         

        Espero sirva de ayuda.

         

  30. Muchas gracias por compartir este material, me estoy iniciando en el mundo de Java y estos ejemplos me han servido de mucho. Solo por curiosidad, ¿Que software utilizan para crear los UML’s?

    Saludos desde Venezuela!

  31. Gracias de verdad!. Por favor no dejen de subir material como este. Sobra la gente que sabe, pero pocos saben explicar. Sigan asi.

  32. Primero, felicitaciones por la web, ya he leído varios artículos y me han gustado.

    Me asalta una duda:
    que diferencia hay entre el método abstracto entrenamiento() y el método partidoFutbol(), a parte de la obligatoriedad de implementar el método abstracto en la clase hija??

    Ambos métodos se pueden «sobrescribir» en la clases hijas, por lo que no entiendo por qué hacer un método abstracto. Desde mi punto de vista, yo haría siempre los métodos en la clase padre, y si la clase hija necesita modificarlo, que lo haga, como en partidoFutbol().

    Supongamos que extendemos 10 clases de la clase padre, y 3 de ellas utilizan el mismo método entranamiento(). Al ser abstracto debería implementar su código en estas 3 clases y en las otras 7, mientras que si no fuera abstracto, sólo se implementaría en las 7 clases que lo necesitan.

    Nuevamente, gracias por vuestro trabajo.

    1. Hola Juan.
      Lo primero decir que los ejemplo que ponemos en esta web son ejemplo didácticos por lo que es normal que muchas veces los ejemplos no tengan un sentido real y te asalten dudas como la que planteas ya que esa misma duda creo que la hemos tenido todos en su momento. Dicho lo cual paso a dar la explicación.

      Las clases abstractas se utilizan para crear «prototipos» de clases; es decir, que las clases que implementen esa interfaz tienen que tener los mismos métodos pero no tienen porque comportarse igual. En el ejemplo lo que quería trasmitir es que tanto un entrenador como un futbolista tienen que «entrenar()» y tienen que jugar un «partidoFutbol()» pero lo hacen de diferente manera ya que un futbolista en un entrenamiento debe de correr, ejercitarse, hacer ejercicios con el balón, etc y un entrenador no corre ni se ejercita ni nada de eso solo dirige el entrenamiento diciendo cuanto tienen que correr, como ejercitarse, las tácticas, estrategias y demás. Como ves tanto futbolista como entrenador tienen un método «entrenamiento()» pero en ese método se implementa de forma distinta. La idea de las clases abstractas es esa que creas un «prototipo» de clase y luego las clases hijas se implementan como sea.

      Se que lo que explico es un concepto algo complejo de entender y que siéndote sincero yo en mi vida profesional he aplicado muy poco las clases abstractas pero es un concepto que hay que «saber que existe» y aplicarlo debidamente ya que es muy potente y nos permite tener un control de nuestro diseño muy importante.

      Por otro lado no te aconsejo que hagas una implementación por defecto en la clase padre y que luego las clases hijas implemente lo que les apetezca ya que eso es (en mi opinión) no es una buena práctica y te provocará mucho descontrol en tu aplicación.

      Espero haberte respondido a tu duda aunque he de decir que el concepto de las clases abstractas es algo complicado de explicar y de entender. Sino te ha quedado claro vuelve a preguntar o abre un nuevo hilo en nuestro foro (http://jarroba.com/foro/) para que no solo nosotros sino más gente te puedan (o nos puedan) ayudar.

      SL2

  33. Gracias por tur aporte, sin embargo, con todo respeto tienes un problema en el diseño del ejemplo
    Herencia tu debes aplicar la frase:»es un(a)» por lo tanto entrenador no es una selección de fútbol tampoco, masajista ni futbolista son una selección de fútbol, en ese caso dices selección de fútbol tiene un entrenador un masajista. Por lo cual tu diseño se aplica más bien a una colaboración o amistad entre las clases.
    Ojo con ese detalle.
    Explicas muy bien el concepto de polimorfismo pero el ejemplo no aplica.
    Es una critica constructiva.
    saludos
    Claudio
    De Chile

    1. Hola Claudio.
      Tienes toda la razón, quizás lo explique así porque al querer explicar el polimorfismo de una manera diferente a como lo explican en otros lados, se me pasaron por altos algunos detalles como ese, que son muy importantes. Muchas gracias por el aporte y cualquier critica constructiva es bienvenida en esta web.
      Saludos.
      PD: Voy a cambiarlo en la entrada lo que nos has dicho.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies

ACEPTAR
Aviso de cookies