jueves, 7 de diciembre de 2017

Interfaces

Un interfaz es una lista de acciones que puede llevar a cabo un determinado objeto. Sorpresa, ¿eso no eran los métodos que se definen en una clase? Casi, en una clase además de aparecer los métodos aparecía el código para dichos métodos, en cambio en un interfaz sólo existe el prototipo de una función, no su código.
Veamoslo con un ejemplo: Pensemos en un interfaz que en su lista de métodos aparezcan los métodos despegar, aterrizar, servirComida y volar. Todos pensamos en un avión, ¿verdad? El motivo es sencillamente que avión es el concepto que engloba las acciones que hemos detallado antes, a pesar que existan muchos objetos avión diferentes entre sí, por ejemplo Boeing 747, Boeing 737, MacDonell-Douglas.
Lo realmente interesante es que todos ellos, a pesar de pertenecer a clases distintas, poseen el interfaz avión, es decir poseen los métodos detallados en la lista del interfaz avión.
Esto significa también que a cualquier avión le podemos pedir que vuele, sin importarnos a que clase real pertenezca el avión, evidentemente cada clase especificará como volará el avión (porque proporciona el código de la función volar).
En java un interfaz define la lista de métodos, pero para que una clase posea un interfaz hay que indicar explícitamente que lo implementa mediante la claúsula implements. Pero veamos primero la estructura de un interfaz:
          [modif.visibilidad] interface nombreInterfaz [extends listaInterfaces]
         {
          prototipo método1;
          
..
          prototipo método1;
         }
         
Donde modif.visibilidad puede ser public o bien sin especificar, es decir visibilidad pública (desde cualquier clase se puede emplear el interfaz) o de paquete (sólo se puede emplear desde clases del mismo paquete).
nombreInterfaz por convenio sigue las mismas reglas de nomenclatura que las clases, y en muchos casos acaba en able (que podíamos traducir como: ser capaz de).
La claúsula opcional extends se emplea para conseguir que un interfaz hereda las funciones de otro/s interfaces, simplemente listaInterfaces es una lista separaada por coma de interfaces de los que se desea heredar.
En muchas ocasiones un interfaz es empleado para definir un comportamiento, que posteriormente será implementado por diversas clases, que podrían no tener nada que ver entre ellas, pero que todas se comportarán igual de cara al interfaz. Es decir, todas tendrán las funciones indicadas por el interfaz.
Cuando varios objetos de distintas clases pueden responder al mismo mensaje (función), aún realizando cosas distintas se denomina polimorfismo.
Prácticas:
Vamos a definir el interfaz Cantante, un interfaz muy simple que sólo posee un método: cantar.

  • Crear el fichero Cantante.java
  • Agregar el siguiente código:
            public interface Cantante
            {
             public void cantar();
            }
            
  • Cojamos la clase Persona y hagamos que implemente el interfaz Cantante:
            public class Persona implements Cantante
            
  • Además agreguemos el código para el método que define el interfaz cantante:
            public void cantar()
            {
                  System.out.println("La laa la raa laaa!");
            }
            
  • Construyamos ahora una clase con función main (ArranqueInterfaz.java)para ejecutar:
            public class ArranqueInterfaz
            {
             public static void main(String arg[])
             {
              
              Persona p = new Persona();
              hacerCantar(p);
             }
             public static void hacerCantar(Cantante c)
             {
              c.cantar();
             }
            }
            
  • Podemos ver que construimos un objeto (p) de tipo persona y se lo pasamos a la función hacerCantar. Esta función espera recibir un objeto Cantante, y una persona lo es, por tanto la recibe y llama al método cantar del objeto recibido.
  • Probemos a intentar pasar a la función hacerCantar en lugar del objeto Persona (p) un objeto String (texto), resultado: error de compilación.
  • Contruyamos ahora la clase Canario (Canario.java), pensando que también sabe cantar:
            public class Canario implements Cantante
            {
             private int peso;
             /* Aqui vendrían el resto de atributos y funciones propias de un canario */
             

             public void cantar()
             {
              System.out.println("Pio Pio Pio");
             }
            }
            
  • Y ahora agreguemos en la clase ArranqueInterfaz el siguiente código, para crear un objeto canario y pasarselo a la función hacerCantar:
            Canario c = new Canario();
            hacerCantar(c);
            
  • Tras ejecutar comprobaremos que podemos pasar tanto una Persona como un Canario a la función hacerCantar, de tal manera que dentro de dicha función sólo accedamos a las funciones del interfaz y no habrá problemas. Por ejemplo, si pusieramos:
            c.SetNombre("Luis")
            
  • dentro de la función hacerPersona, podría funcionar si pasasemos un objeto Persona, pero no si pasamos uno de tipo Canario.

martes, 5 de diciembre de 2017

Mostrar imagen dentro un jPanel

Se crea un nuevo proyecto en Netbeans y en vista de diseño se agrega un jButton y un jPanel.
Una vez hecho esto se crea una nueva carpeta llamada "Images". Dentro esta carpeta le agregamos una imagen en formato .PNG (se inserta directamente arrastrando la imagen a la carpeta).





















Codigo 1. (Principal.java):

package Imagen1;

public class Principal extends javax.swing.JFrame {
    
    public Principal() {
        initComponents();
        this.setLocationRelativeTo(null); // Centrar pantalla 
    }
                            
    private void initComponents() { ... // Codigo generado automaticamente.                        

    private void jButtonIniciarActionPerformed(java.awt.event.ActionEvent evt) {
        Imagen im = new Imagen(jPanel1);
        jPanel1.add(im).repaint();
    }                                              

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new Principal().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JButton jButtonIniciar;
    private javax.swing.JPanel jPanel1;
    // End of variables declaration                   

}


Codigo 2. (Imagen.java):

package Imagen1;

import java.awt.Graphics;
import javax.swing.ImageIcon;
import javax.swing.JPanel;

public class Imagen extends javax.swing.JPanel {
    int x, y;

    public Imagen(JPanel jPanel1) {
        this.x = jPanel1.getWidth();
        this.y = jPanel1.getHeight();
        this.setSize(x, y);
    }

    @Override
    public void paint(Graphics g) {
        ImageIcon Img = new ImageIcon(getClass().getResource("/Images/imagen1.png"));
        g.drawImage(Img.getImage(), 0, 0, x, y, null);
    }    

}


Resultado:

SOLID

S.O.L.I.D
Single Responsibility
Open Closed
Liskov Substitution
Interface Segregation
Dependency Inversion

1.- Single Responsibility Principle
Una clase jamás debería tener mas de una razón por la cual cambiar.
·         Responsabilidad= Razón para cambiar
·         Si una Clase asume mas de una responsabilidad, entonces tendrá mas de una razón para cambiar.
·         Acoplamiento de responsabilidades.



2.- Open Closed Principle
Entidades de software deberían estar abiertas para extensión pero cerradas para modificación
·         Si 1 cambio a varios módulos, entonces la aplicación no esta bien diseñada.
·         Debemos diseñar módulos que nunca cambien.
Cuando realizas un sistema de una empresa y tienes que tenerun ril basado “puede realizar un pago, mañana me solicitan que cree un nuevo rol, por ejemplo “devolución” . Lasolucion actual seria crear un método mas, pero el problema radica en que nuestra clase tiene que impremntar mas código. “abierto para la extensión pero cerrado para la modificación. Por lo tanto no modifiques nada y extiende la funcionalidad.
3.- Principio de Sustitución de Liskov
El principio de Liskov nos ayuda a utilizar la herencia de forma correcta.
Bien, tenemos una clase base abstracta que será un Robot y este puede realizar una cantidad X de punto de año al momento de atacar, pero necesita ser heredada par ser usada.
Asimismo, la tecnología avanzado y llegaron 2 modelos nuevo pero van a heredar las características del Robot. La diferencia es que estos tienen más poder de ataque, por lo tanto habremos que sobrescribir sus métodos.
4.- Principio de Segregación de Interfaces
Nos ayuda a no obligar a ninguna clase a implementar métodos que no utiliza.

5.- Principio de Inversión de Dependencias
Gracias al principio de inversión de dependencias, podemos hacer que el código que es el núcleo de nuestra
aplicación no dependa de los detalles de implementación, como pueden ser el framework que utilices,
 la base de datos, cómo te conectes a tu servidor


jueves, 30 de noviembre de 2017

Eficiencia

Tiempo de ejecución y eficiencia de algoritmos

En esta sección vamos a estudiar algunas formas de medir la eficiencia de nuestros programas en Sage, y a estudiar la complejidad de las operaciones usuales en Sage.
Medir de forma precisa este tiempo no es una tarea trivial, y los resultados pueden variar sensiblemente de un ordenador a otro. La cantidad de factores que pueden influir en el tiempo de ejecución es muy larga:
  • algoritmo usado
  • sistema operativo
  • velocidad del procesador, número de procesadores y conjunto de instrucciones que entiende
  • cantidad de memoria RAM, y caché, y velocidad de cada una
  • coprocesador matemático, GPU
  • ...
Incluso en la misma máquina, el mismo algoritmo tarda algunas veces mucho más tiempo en dar el resultado que otras, debido a factores como el tiempo que consumen las otras aplicaciones que se están ejecutando, o si hay suficiente memoria RAM en el momento de ejecutar el programa.
Nuestro objetivo es comparar sólo los algoritmos , intentando sacar conclusiones independientes de la máquina.
Un mismo algoritmo se puede llamar con distintos datos de entrada. Nuestro objetivo es estudiar el tiempo de ejecución como función del “tamaño” de los datos de entrada . Para ello usamos dos técnicas:
  • Medir tiempos de ejecución de los programas con datos de entrada de distintos tamaños
  • Contar el número de operaciones que realiza el programa


Usar el siguiente código para medir la velocidad con la que procede el algoritmo

miércoles, 15 de noviembre de 2017

ArrayList en Java: Recorriendo y eliminando elementos

La clase ArrayList es una de las tantas que implementa la implementa la interfaz List. Por lo tanto es una colección ordenadaque puede contener elementos duplicados. A no confundir, cuando digo ordenada me refiero al orden en que se encuentran los elementos dentro de la lista, el cual es independiente a que los elementos se encuentren ordenados por algún criterio, como por ejemplo de menor a mayor.
La clase ArrayList es redimensionable, esto quiere decir que puede crecer o achicarse en tiempo de ejecución, aunque esto es una operación ineficiente cuando se desea insertar o eliminar elementos que se encuentran en el medio, ya que todos los elementos que se encuentran después de este deben ser desplazados. En este caso es mejor usar otras estructuras o clases como LinkedList.
ArrayList comparte muchos comportamientos con la clase Vector y se diferencian en que la primera tiene mejor performance, siempre y cuando no se necesite trabajar con operaciones sincronas.
Pero bueno, basta de tanta introducción. Querían código? Código tendran.
El siguiente ejemplo, Muestra Diferentes formas de recorrer una lista, la antigua forma usando for, con un for mejorado (enhanced for), y usando Iterator. Asi también muestra como eliminar elementos de una lista
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public class ArrayListExample {

 public static void main(String[] args){

  String[] bands = {"Nirvana", "Jamiroquai",
                                     "Guns and Roses", "Funkadelic Parliament"};
  List<String> bandList = new ArrayList<String>();

  //add elements from String array to List
  for (String band : bands){
   bandList.add(band);
  }

  String[] undesirableBands = {"Nirvana","Led Zepelling",
                                              "The Strokes", "Jamiroquai"};
  List<String> undesirableList = new ArrayList<String>();

  //add elements from String array to List
  for (String undesirableBand : undesirableBands){
   undesirableList.add(undesirableBand);
  }

  System.out.println("List of bands has: ");
  for (int count = 0; count < bandList.size(); count++){
   System.out.printf("%s ", bandList.get(count));
  }

  removeBands(bandList, undesirableList);
  System.out.println("\n\nI just wish these bands: ");

  for (int count = 0; count < bandList.size(); count++){
   System.out.printf("%s ", bandList.get(count));
  }
 }

 public static void removeBands(Collection<String> bands,
                                         Collection<String> undesirableBands){
  Iterator<String> iteratorBand = bands.iterator();

  while(iteratorBand.hasNext()){
   if(undesirableBands.contains(iteratorBand.next())){
    iteratorBand.remove();
   }
  }
 }
}






Empecemos a analizar el código:

String[] bands = {"Nirvana", "Jamiroquai",
                       "Guns and Roses", "Funkadelic Parliament"};
  List<String> bandList = new ArrayList<String>();
Primero lo que hacemos es declarar e inicializar un arreglo de String bands que contendra los nombres de las bandas.
Una clase ArrayList es una clase generica y por lo tanto podemos especificarle el tipo de argumento que indican los elementos que la misma contendrá.
Pero, por qué declaramos bandList de tipo List? Simple, esto hace que nuestro código sea mas flexible para modificarlo. Si más tarde desearamos cambiar la implementación a LinkedList podríamos hacerlo solo modificando el lado derecho de la expresión.

Recorrer usando for mejorado

La primera forma de recorrer una lista es esta (Enhanced For Loop):
for (String band : bands){
bandList.add(band);
}

Esta forma de recorrer una lista esta disponible desde la versión Java SE 5.0. Su estructura es simple y permite recorrer todos y cada uno de los elementos de un arreglo/colección (recordar que una Lista es también una colección) en el orden en que se encuentran.
No hay mayores comentarios acerca del método add, este simplemente agrega un elemento a bandList.
De la misma forma se agregan los elementos a la lista undesirableBand. Esta, la vamos a usar para eliminar elementos de la primera
//add elements from String array to List
        for (String undesirableBand : undesirableBands){
            undesirableList.add(undesirableBand);
        }


Recorrer usando for clásico

La segunda forma de recorrer una lista es con la ya conocida estructura repetitiva for:
for (int count = 0; count < bandList.size(); count++){
System.out.printf("%s ", bandList.get(count));
}

En este caso, un tanto más engorroso que el primero, usamos métodos propios de la interfaz List. El primero es size() el cual devuelve un número entero con la cantidad de elementos de la lista. El otro método de la interfaz List es get(count) que devuelve el elemento que se encuentra en la posición count.

Recorrer utilizando un Iterador

La tercer forma de recorrer una lista es con un Iterator. La interfaz Iterator permite iterar sobre los elementos de una colección y como mencione anteriormente, una List es un tipo de colección.
El método removeBands(Collection<String> bands, Collection<String> undesirableBands) recibe dos parámetros que pueden ser cualquier tipo de Collections que contengan String. La razon de usar Collection en vez de ArrayList es que le da al método mayor flexibilidad, pudiendo recibir cualquier tipo de objeto que implemente la interfaz Collection.
Tanto la interfaz Collection como Iterator son de tipo genérico.
Analicemos el siguiente fragmento:
Iterator<String> iteratorBand = bands.iterator();

while(iteratorBand.hasNext()){
if(undesirableBands.contains(iteratorBand.next())){
iteratorBand.remove();
}
}

El método iterator() de bands.iterator() devuelve un Iterator de la colección. La interfaz Iterator tiene dos métodos importantes:
  • hasNext(): Este método devuelve true en caso de que existan más elementos
  • next(): Este método nos posiciona en el siguiente elemento del Iterator
El método contains() pregunta si la colección undesirableBands posee el elemento devuelto por iteratorBand.next(), en tal caso lo elimina de iteratorBand con la instrucción iteratorBand.remove()
Para borrar elementos de una colección, la mejor forma es usar Iterator.  Si intentamos hacerlo con el enhanced forobtendremos una excepción de tipo ConcurrentModificationException ya que estamos recorriendo e intentando modificar.
Si una colección es modificada, luego de que se creo el Iterator para la misma, dicho Iterator se vuelve inválido y las operaciones que se quieran realizar sobre el lanzarán excepción de tipo ConcurrentModificationException. Por esto se dice que los iteradores son de fallo rápido.

martes, 24 de octubre de 2017

Serialización de objetos en java

Serialización de un objeto: Implementar Serializable

Para que un programa java pueda convertir un objeto en un montón de bytes y pueda luego recuperarlo, el objeto necesita ser Serializable. Al poder convertir el objeto a bytes, ese objeto se puede enviar a través de redguardarlo en un fichero, y después reconstruirlo al otra lado de la red, leerlo del fichero.Para que un objeto sea serializable basta con que implemente la interfaz Serializable
Como la interfaz Serializable no tiene métodos, es muy sencillo implementarla, basta con un implements Serializable y nada más. Por ejemplo, la clase Datos siguiente es Serializable y java sabe perfectamente enviarla o recibirla por red.

public class Datos implements Serializable
{
   public int a;
   public String b;
   public char c;
}
Si dentro de la clase hay atributos que son otras clases, éstos a su vez también deben ser Serializable. Con los tipos de java (StringInteger, etc.) no hay problema porque lo son. Si ponemos como atributos nuestras propias clases, éstas a su vez deben implementar Serializable. Por ejemplo
/* Esta clase es Serializable porque implementa Serializable y todos sus
 *  campos son Serializable, incluido "Datos f;"
 */
public class DatoGordo implements Serializable
{
   public int d;
   public Integer e;
   Datos f;
}

Serialización a medida

En realidad, se llama "serializar un objeto" al proceso de convertirlo a bytes, para poder enviarlo por una red, y reconstruirlo luego a partir de esos bytes.
A veces podemos necesitar que se haga algo especial en el momento de serializarlo, bien al construirlo a bytes, bien al recibirlo. Por ello java nos permite hacerlo. Basta con hacer que nuestro objeto defina uno o los dos métodos siguientes:
private void readObject(java.io.ObjectInputStream stream)
     throws IOException, ClassNotFoundException
{
   // Aqui debemos leer los bytes de stream y reconstruir el objeto
}

private void writeObject(java.io.ObjectOutputStream stream)
     throws IOException
{
   // Aquí escribimos en stream los bytes que queramos que se envien por red.
}

viernes, 13 de octubre de 2017

TIPO DE DATOS, VARIABLES Y OPERADORES

TIPO DE DATOS, VARIABLES Y OPERADORES

TIPO DE DATO:

Hay 8 tipos de datos primitivos que podemos clasificar en: tipo numéricos y tipos boolean. A su vez, los tipos numéricos se clasifican en tipos enteros y tipos reales.
Tipos enteros: byte, short, int, long y char.
Tipo reales: float y double.
Cada tipo primitivo tiene un rango diferente de valores positivos y negativos, excepto el boolean que  solo tiene dos valores: true y false. El tipo de dato que seleccione para declarar las variables de un determinado programa dependerá del rango y tipo de valores que vayan a almacenar una de ellas y de si estos son enteros o fraccionarios.















VARIABLES: 

Una variable es un nombre que se asocia con una porción de la memoria del ordenador, en la que se guarda el valor asignado a dicha variable. Hay varios tipos de variables que requieren distintas cantidades de memoria para guardar datos.
Todas las variables han de declararse antes de usarlas, la declaración consiste en una sentencia en la que figura el tipo de dato y el nombre que asignamos a la variable. Una vez declarada se le podrá asignar valores.
Java tiene tres tipos de variables:
  • de instancia
  • de clase
  • locales
Las variables de instancia o miembros dato como veremos más adelante, se usan para guardar los atributos de un objeto particular.

Las variables de clase o miembros dato estáticos son similares a las variables de instancia, con la excepción de que los valores que guardan son los mismos para todos los objetos de una determinada clase. En el siguiente ejemplo, PI es una variable de clase y radio es una variable de instancia. PI guarda el mismo valor para todos los objetos de la clase Circulo, pero el radio de cada círculo puede ser diferente
class Circulo{
               static final double PI=3.1416;
               double radio;
//...
}
Las variables locales se utilizan dentro de las funciones miembro o métodos. En el siguiente ejemplo area es una variable local a la función calcularArea en la que se guarda el valor del área de un objeto de la claseCirculo. Una variable local existe desde el momento de su definición hasta el final del bloque en el que se encuentra.
class Circulo{
//...
               double calcularArea(){
                               double area=PI*radio*radio;
                               return area;
               }
}

Información extraída de--->
https://aprendiendojee.wordpress.com/2010/07/30/variables-y-constantes/

OPERADORES:

Operadores java aritméticos:
·         + Suma. Los operadores pueden ser enteros o reales
·         -  Resta. Los operadores pueden ser enteros o reales
·         *  Multiplicación. Los operadores pueden ser enteros o reales
·         /   División. los operadores pueden ser enteros o reales. Si ambos son enteros el resultado es entero. En cualquier otro caso el resultado es real.
·         %  Resto de la división. Los operadores pueden ser de tipo entero o real.

Operadores java relacionales: comparan dos operadores y dan como resultado de la comparación verdadero o falso.
·         <      Menor que
·         >      Mayor que
·         <=    Menor o igual
·         >=    Mayor o igual
·         !=     Distinto
·         ==    Igual

Operadoresjava lógicos: se utilizan con operadores de tipo boolean. Se utilizan para construir expresiones loicas, cuyo resultado es de tipo true o false.
·         &&   AND. El resultado es verdadero si las dos operaciones son verdaderos. El resultado es falso en caso contrario. Si el primer operando es falso no se evalúa el segundo, ya que el resultado será falso.
·         ||      OR. El resultado es falso si los dos operadores son falsos. Si uno es verdadero el resultado es verdadero

          Si el primer operadores es verdadero no se evalúa el segundo.
·         !      NOT. Se aplica sobre un solo operador. Cambia el valor del operador de verdadero a falso y viceversa.
.
            Información extraída de--->

Polimorfismo

Polimorfismo en Java con ejemplos


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 “muchasformas”. 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.

http://jarroba.com/polimorfismo-en-java-parte-i-con-ejemplos/

Interfaces

Un interfaz es una lista de acciones que puede llevar a cabo un determinado objeto. Sorpresa, ¿eso no eran los métodos que se definen en un...