Herencia en Java
La herencia es un pilar importante de OOP (Programación Orientada a Objetos). Es el mecanismo en Java por el cual una clase permite heredar las características (atributos y métodos) de otra clase. ... Hereda todas las variables y métodos definidos por la superclase y agrega sus propios elementos únicos.
La Herencia es uno de los 4 pilares de la programación orientada a objetos (POO) junto con la Abstracción, Encapsulación y Polimorfismo. Al principio cuesta un poco entender estos conceptos característicos del paradigma de la POO porque solemos venir de otro paradigma de programación como el paradigma de la programación estructurada (ver la entrada"Paradigmas de Programación), pero se ha de decir que la complejidad está en entender este nuevo paradigma y no en otra cosa. En esta entrada vamos a explicar de la mejor manera posible que es la herencia y lo vamos a explicar con un ejemplo.
Respecto a la herencia se han dado muchas definiciones como por ejemplo la siguiente: "La herencia es un mecanismo que permite la definición de una clase a partir de la definición de otra ya existente. La herencia permite compartir automáticamente métodos y datos entre clases, subclases y objetos.". Así de primeras esta definición es un poco difícil de digerir para aquellos que estéis empezando con la POO, así que vamos a intentar digerir esta definición con un ejemplo en el que veremos que la herencia no es más que un "Copy-Paste Dinámico" o una forma de "sacar factor común" al código que escribimos.
El ejemplo que proponemos es un caso en el que vamos a simular el comportamiento que tendrían los diferentes integrantes de la selección española de futbol; tanto los Futbolistas como el cuerpo técnico (Entrenadores, Masajistas, etc…). Para simular este comportamiento vamos a definir tres clases que van a representaran a objetos Futbolista, Entrenador y Masajista. De cada unos de ellos vamos a necesitar algunos datos que reflejaremos en los atributos y una serie de acciones que reflejaremos en sus métodos. Estos atributos y métodos los mostramos en el siguiente diagrama de clases:
Como se puede observar, vemos que en las tres clases tenemos atributos y métodos que son iguales ya que los tres tienen los atributos id, Nombre, Apellidos y Edad; y los tres tienen los métodos de Viajar y Concentrarse:
A nivel de código tenemos lo siguiente tras ver el diagrama de clases:
public class Futbolista { private int id; private String Nombre; private String Apellidos; private int Edad; private int dorsal; private String demarcacion; // constructor, getter y setter public void Concentrarse() { ... } public void Viajar() { ... } public void jugarPartido() { ... } public void entrenar() { ... } } | public class Entrenador { private int id; private String Nombre; private String Apellidos; private int Edad; private String idFederacion; // constructor, getter y setter public void Concentrarse() { ... } public void Viajar() { ... } public void dirigirPartido() { ... } public void dirigirEntreno() { ... } } | public class Masajista { private int id; private String Nombre; private String Apellidos; private int Edad; private String Titulacion; private int aniosExperiencia; // constructor, getter y setter public void Concentrarse() { ... } public void Viajar() { ... } public void darMasaje() { ... } } |
Lo que podemos ver en este punto es que estamos escribiendo mucho código repetido ya que las tres clases tienen métodos y atributos comunes, de ahi y como veremos enseguida, decimos que la herencia consiste en "sacar factor común" para no escribir código de más, por tanto lo que haremos sera crearnos una clase con el "código que es común a las tres clases" (a esta clase se le denomina en la herencia como "Clase Padre o SuperClase") y el código que es especifico de cada clase, lo dejaremos en ella, siendo denominadas estas clases como "Clases Hijas", las cuales heredan de la clase padre todos los atributos y métodos públicos o protegidos. Es muy importante decir que las clases hijas no van a heredar nunca los atributos y métodos privados de la clase padre, así que mucho cuidado con esto. En resumen para que veáis la ventaja de la herencia, tenemos ahora una clase padre con 'n' lineas de código y tres clases hijas con 'a', 'b' y 'c' lineas de códigos respectivamente, por tanto si hecháis cuentas, hemos reducido nuestro código en '2n' líneas menos ya que antes teníamos '(n+a)+(n+b)+(n+c)' líneas de código y ahora tras aplicar herencia tenemos 'n+a+b+c' líneas, aunque también es cierto que tenemos una clase más, pero veremos un poco más adelante la ventaja de tener esa clase padre. En resumen, al "sacar factor común" y aplicar herencia, tenemos las siguientes clases:
A nivel de código, las clases quedarían implementadas de la siguiente forma:
public class SeleccionFutbol { protected int id; protected String Nombre; protected String Apellidos; protected int Edad; // constructor, getter y setter public void Concentrarse() { ... } public void Viajar() { ... } } | ||
public class Futbolista extends SeleccionFutbol { private int dorsal; private String demarcacion; public Futbolista() { super(); } // getter y setter public void jugarPartido() { ... } public void entrenar() { ... } } | public class Entrenador extends SeleccionFutbol { private String idFederacion; public Entrenador() { super(); } // getter y setter public void dirigirPartido() { ... } public void dirigirEntreno() { ... } } | public class Masajista extends SeleccionFutbol { private String Titulacion; private int aniosExperiencia; public Masajista() { super(); } // getter y setter public void darMasaje() { ... } } |
Como podéis observar ahora queda un código mucho más limpio, estructurado y con menos líneas de código, lo que lo hace más legible, cosa que es muy importante y lo que todavía lo hace más importante es que es un código reutilizable, lo que significa que ahora si queremos añadir más clases a nuestra aplicación como por ejemplo una clase Médico, Utiller@, Jefe/a de prensa etc. que pertenezcan también al equipo técnico de la selección Española, lo podemos hacer de forma muy sencilla ya que en la clase padre (SeleccionFutbol) tenemos implementado parte de sus datos y de su comportamiento y solo habrá que implementar los atributos y métodos propios de esa clase. ¿Empezáis a ver la utilidad de la herencia?.
Ahora si os habéis fijado bien en el código que se ha escrito y sino habéis tenido experiencia con la herencia en Java, habréis podido observar dos palabras reservadas "nuevas" como son "extends", "protected" y "super". Pues bien, ahora vamos a explicar el significado de ellas:
- extends: Esta palabra reservada, indica a la clase hija cual va a ser su clase padre, es decir que por ejemplo en la clase Futbolista al poner "public class Futbolista extends SeleccionFutbol" le estamos indicando a la clase 'Futbolista' que su clase padre es la clase 'SeleccionFutbol' o dicho de otra manera para que se entienda mejor, al poner esto estamos haciendo un "copy-paste dinámico" diciendo a la clase 'Futbolista' que se 'copie' todos los atributos y métodos públicos o protegidos de la clase 'SeleccionFutbol'. De aquí viene esa 'definición' que dimos de que la herencia en un 'copy-paste dinámico'.
- protected: sirve para indicar un tipo de visibilidad de los atributos y métodos de la clase padre y significa que cuando un atributo es 'protected' o protegido, solo es visible ese atributo o método desde una de las clases hijas y no desde otra clase.
- super: sirve para llamar al constructor de la clase padre. Quizás en el código que hemos puesto no se ha visto muy bien, pero a continuación lo mostramos de formas más clara, viendo el constructor de los objetos pasándole los atributos:
public class SeleccionFutbol { ...... public SeleccionFutbol() { } public SeleccionFutbol(int id, String nombre, String apellidos, int edad) { this.id = id; this.Nombre = nombre; this.Apellidos = apellidos; this.Edad = edad; } ......
public class Futbolista extends SeleccionFutbol { ...... public Futbolista() { super(); } 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; } ......
Hasta aquí todo correcto, pero ahora vamos a ver como trabajamos con estas clases. Para ver este funcionamiento de forma clara y sencilla vamos a trabajar con un objeto de cada clase y vamos a ver como se crean y de que forma ejecutan sus método. 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) { Entrenador delBosque = new Entrenador(1, "Vicente", "Del Bosque", 60, "284EZ89"); Futbolista iniesta = new Futbolista(2, "Andres", "Iniesta", 29, 6, "Interior Derecho"); Masajista 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(); } ......
Lo primero que vemos es que nos creamos un objeto de cada clase, pasándole los atributos al constructor como parámetro y después "sorprendentemente" los metemos en un "ArrayList" de objetos de la clase "SeleccionFutbol" que es la clase padre. Esto evidentemente te lo permite hacer ya que todos los objetos son hijos de la misma clase padre. Luego como veis, recorremos el ArrayList y ejecutamos sus métodos "comunes" como son el 'Concentrarse' y el 'Viajar'. Este código da como salida lo siguiente:
Todos los integrantes comienzan una concentracion. (Todos ejecutan el mismo método) Vicente Del Bosque -> Concentrarse Andres Iniesta -> Concentrarse Raúl Martinez -> Concentrarse Todos los integrantes viajan para jugar un partido. (Todos ejecutan el mismo método) Vicente Del Bosque -> Viajar Andres Iniesta -> Viajar Raúl Martinez -> Viajar
Como veis al ejecutar todos el mismo método de la clase padre el código puesto funciona correctamente.
Posteriormente vamos a ejecutar código especifico de las clases hijas, de ahi que ahora no podamos recorrer el ArrayList y ejecutar el mismo método para todos los objetos ya que ahora esos objetos son únicos de la clases hijas. El código es el siguiente:
// ENTRENAMIENTO System.out.println("nEntrenamiento: Solamente el entrenador y el futbolista tiene metodos para entrenar:"); System.out.print(delBosque.getNombre()+" "+delBosque.getApellidos()+" -> "); delBosque.dirigirEntrenamiento(); System.out.print(iniesta.getNombre()+" "+iniesta.getApellidos()+" -> "); iniesta.entrenar(); // MASAJE System.out.println("nMasaje: Solo el masajista tiene el método para dar un masaje:"); System.out.print(raulMartinez.getNombre()+" "+raulMartinez.getApellidos()+" -> "); raulMartinez.darMasaje(); // PARTIDO DE FUTBOL System.out.println("nPartido de Fútbol: Solamente el entrenador y el futbolista tiene metodos para el partido de fútbol:"); System.out.print(delBosque.getNombre()+" "+delBosque.getApellidos()+" -> "); delBosque.dirigirPartido(); System.out.print(iniesta.getNombre()+" "+iniesta.getApellidos()+" -> "); iniesta.jugarPartido();
Como vemos aunque el entrenador y los futbolistas asistan a un entrenamiento, los dos hacen una función diferente en el mismo, por tanto hay que hacer métodos diferente para cada una de las clases. Ya veremos cuando hablemos del polimorfismo que podremos ejecutar el mismo método para clases diferentes y que esos métodos hagan cosas distintas. Como resultado al código mostrado tenemos lo siguiente:
Entrenamiento: Solamente el entrenador y el futbolista tiene metodos para entrenar: Vicente Del Bosque -> Dirige un entrenamiento Andres Iniesta -> Entrena Masaje: Solo el masajista tiene el método para dar un masaje: Raúl Martinez -> Da un masaje Partido de Fútbol: Solamente el entrenador y el futbolista tiene metodos para el partido de fútbol: Vicente Del Bosque -> Dirige un partido Andres Iniesta -> Juega un partido
CONCLUSIONES Y ACLARACIONES:
Esto ha sido todo lo que hemos contado sobre la herencia en esta entrada. El tema de la herencia es un tema que puede ser un poco más complejo de lo que lo hemos contado aquí, ya que solo hemos contado lo que es la herencia simple (ya que Java por el momento es el único tipo de herencia que soporta) y no la herencia múltiple, que es un tipo de herencia en la que una clase hija puede tener varios padres, aunque por el momento si estáis empezando a aprender el concepto de la herencia, con la herencia simple tenéis más que suficiente. Para los que os estéis iniciando en el mundo de la ingeniería informática, habréis podido ver que hemos puesto unos ejemplo mostrando unos diagramas "un poco raros"; pues bien, estos diagramas se llaman diagramas de clases (que los hemos realizado con la herramienta web de www.genmymodel.com) y sirven para representar de forma gráfica los atributos y métodos de las clases y las relaciones entre ellos, utilizando el lenguaje UML del cual intentaremos hablar más adelante en otros tutoriales. Por último decir y aclarar que en esta entrada quizás no hemos utilizado una terminología correcta para explicar la herencia, pero lo hemos explicadode una forma algo distinta a como esta explicada por ahi para que los que empeceis podais entender la herencia desde otro punto de vista.
les doy otro alcance
Herencia en Java, 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
Realmente soy novato, es mi primer año estudiando sistemas y comencé con Java.
Mi duda es: Cual es la diferencia entre heredar o extender e importar una clase. (?)
De ante mano, gracias.
«extend» es heredar en Java (una clase puede heredar de otra clase en Java).
Importar una clase sirve para utilizar una clase que esté en otro fichero de Java separado del que estás utilizando.
| |
| |
| |
hijo hijo
Si te es útil, podrías heredar con una clase padre a una hija y desde esta hija (que sería el nuevo padre) que herede otra hija.
Necesito asesoría para un proyecto que estoy desarrollando; me podría regalar su numero de contacto para comunicarme o correo electrónico?
Teniendo los goles de los futbolistas puedes sumarlos por equipos y devolverlos. Por otro lado, teniendo cada futbolista un listado de los partidos que juegan también tendrías esa información.
puedo utilizar la clase Fecha desde otro proyecto con el comado import encapsulamientofecha.Fecha; o la herencia solo puede ser aplicada a clases del mismo proyecto como en este ejemplo mostrado
Martin.
Se cambia la clase SeleccionFutbol a abstracta en el siguiente artículo sobre polimorfismo en: https://jarroba.com/polimorfismo-en-java-parte-i-con-ejemplos/
-run
-nombre
-apellido
-telefono
-email
vehiculo:
-matricula
-marca
-modelo
-color
-año
servicio:
-serviciocontratado
Seria de mucha ayuda si me lograsen ayudar o si tienen una mejor opcion a realizar… en estos momentos estoy trabajando con las 3 clases por separado dentro programa.
gracias y saludos.
void metodo_del_padre(){
// Código del método del padre…
}
}
void metodo_de_la_interfaz();
};
@Override
void metodo_de_la_interfaz(){
// Implementar…
}
}
@Override
void metodo_de_la_interfaz(){
// Implementar…
}
}
List<Persona> listaDePersonas = new ArrayList<Persona>();
listaDePersonas.add(estudiante);
anilistaDePersonas.add(docente );
for (Persona persona: listaDePersonas) {
persona.metodo_del_padre();
persona.metodo_de_la_interfaz();
}
-DOCENTE hereda de PERSONA
-ESTUDIANTE hereda de PERSONA
public class ClasePadre
{
...
}
}
public class ClaseHija extends ClasePadre
{
@Override
public void MiMetodoClasePadre() {
...
}
}
En el Main o donde quieras, si instancias a la ClasePadre utilizarás el método original.
ClasePadre objetoPadre = new ClasePadre();
objetoPadre.MiMetodoClasePadre()
Sin embargo, si instancias la ClaseHija utilizarás el método sobrescrito (y no el de ClasePadre):
ClasePadre ClaseHija = new ClaseHija();
ClaseHija.MiMetodoClasePadre()
// O más correcto para el polimorfismo y desacoplar los módulos:
ClasePadre ClasePadre = new ClaseHija();
ClaseHija.MiMetodoClasePadre()
No hay mas, te felicito, uno descarga libros o los compra , orientados a POO , comienza a leerlos bien feliz y BAM te empiezan a enrollar demasiado con los conceptos, con los «ejemplos» (que en su mayoría son de nominas) y ya para la hoja 300 no entendiste ni maíz, pero tu , aqui con tu ejemplo prácticamente resumiste 56 hojas de lo mismo , y mejor aun , diste un ejemplo mas «realista».
Los artículos que publicamos procuramos que sean lo más resumidos posibles y que aporten lo máximo con ejemplos reales.
Cuando alguien instancia un objeto de la clase hija (en este ejemplo «Futbolista»), por ejemplo:
Futbolista MiFutbolista = new Futbolista(1, "mi nombre", "mi apellido", 25, 100, "mi demarcacion");
Lo primero que hará este objeto será llamar al constructor de la clase padre (en este caso «SeleccionFutbol») para instanciar el objeto del que hereda. Se instancia llamando a super() sería, que desde el objeto de la clase hija sería como si hiciese:
SeleccionFutbol MiSeleccionFutbol = new SeleccionFutbol();
O en el otro ejemplo del segundo constructor con parámetros, el super(id, nombre, apellidos, edad) sería como si el objeto de la clase hija lo instanciara así:
SeleccionFutbol MiSeleccionFutbol = new SeleccionFutbol(1, "mi nombre", "mi apellido", 25);
Es decir, depende del constructor de la clase hija al que llamemos, llamaremos a uno u otro de la clase padre. En la clase padre si no se pone el constructor vacío es como si lo pones vacío (simplemente ocupa más código).
Está excelente, con las analogías está más claro, llevo buen tiempo tratando con libros, de hecho me brinque el java por otros lenguajes, como bien dices lo difícil es entender los conceptos de paradigma, utilice java solo para el acceso a bases de datos, pero ahora voy a sacar provecho de su potencial.
Saludo
Desde Ecuador
Pero tengo una duda que la he traído por un tiempo, ¿los miembros de clase estáticos pueden ser hederados también a sus subclases? Eso es algo que no he encontrado respuesta aún o.O.
muchas gracias 🙂
– Composición: No muestres public (o protected) ciertas partes de la clase del padre. Estas partes se mantendrán vivas mientras dure el padre y morirán con él (la destrucción de la Clase supone la destrucción de la Parte). Por ejemplo:
dorsal(Atrib)
qué deberìa hacer?
integrantes.add(delBosque);
integrantes.add(iniesta);
integrantes.add(raulMartinez);
Tienes que añadir los add() para introducir cada uno. Si ya tuvieras el objeto «integrantes» en memoria con todo añadido no tienes que volver a añadirlos (pues ya estarán añadidos)
Estoy haciendo una tpv y quiero guardar los objetos en un arraylist. Como voy a tener muchos métodos iguales he decidido hacer una superclase y que de ella se hereden los métodos add, remover…
Tengo clase producto, cliente, venta…. Y todas ellas van a llamar a un método añadir de la superclase. Mi pregunta es:
Donde defino los arraylist? En cada clase? En la superclase? Las paso como parámetros a los métodos y después les hago un return??
Muchas gracias de antemano!
La diferencia de poner getter o setter o poner las variables públicas (o protected si son heredadas) es la seguridad del objeto y por convención de los programadores.
public int noNumeroCero = 1;
Y por cualquier cosa requieres que nunca sea cero (por ejemplo, porque vayas a usar la variable como divisor; sabemos que dividir entre cero da error), si pueden acceder desde fuera directamente al ser pública, podrán ponerte un cero y el programa fallar. Si ponemos getter o setter podemos hacer la comprobación en el propio setNoNumeroCero(). Evidentemente, dependiendo de la lógica del programa, se podría hacer la comprobación antes de usar la variable.
SL2
Para hacer los diagramas de clase (en esta entrada) he utilizado esta web https://api.genmymodel.com que te permite crear algunos diagramas con UML.
public String getNombre(){
return this.Nombre;
}
return this.Apellidos;
}
Tienes que hacer los métodos.
Los getter y setter, de establecimiento u obtención de valores de las variables globales, se han obviado para que el artículo no quedara muy denso, y fuera sencillo de entender 🙂
EJemplo.
objeto.metodo1( — aqui necesito enviarle el objeto heredado, es decir el objeto de tipo clase2—);
Saludos.
Una pregunta la clase Futbolista también hereda el Id de la clase padre.?
Es decir al momento de guardar los datos en una base de datos, el Id llave primaria de la clase padre, pasaría
como llave foránea a la clase hija?, o tengo que crear una llave primaria para la clase hija?
Muchas gracias.
En la herencia se heredan todos los atributos de la clase padre, así que el id también lo hereda.
Eso que comentas se llama herencia múltiple y hasta la fecha Java no soporta herencia múltiple. Hay otros lenguajes como C++ que si tienen herencia múltiple pero si te soy sincero hasta la fecha no he hecho nada con herencia múltiple, aunque sabiendo bien el concepto de herencia seria fácil aplicarla.
SL2
Sé que de esta manera, todos aquellos atributos de una clase que vienen precedidos del modificador «protected» serán visibles desde todas las clases hijas de la clase abstracta donde se declara ese atributo protected.
Así pues si en la clase Empleado definimos:
{
protected int sueldo;
. . .
}
La diferencia de poner getter o setter o poner las variables públicas (o protected si son heredadas) es la seguridad del objeto y por convención de los programadores.
public int noNumeroCero = 1;
Y por cualquier cosa requieres que nunca sea cero (por ejemplo, porque vayas a usar la variable como divisor; sabemos que dividir entre cero da error), si pueden acceder desde fuera directamente al ser pública, podrán ponerte un cero y el programa fallar. Si ponemos getter o setter podemos hacer la comprobación en el propio setNoNumeroCero(). Evidentemente, dependiendo de la lógica del programa, se podría hacer la comprobación antes de usar la variable.
Vale, pero si tu me pasas una clase con una serie de atributos protected, yo podré, mediante sus setters cambiar sus valores aunque no sea en ninguna de sus clases hijas… entonces donde está la seguridad? o no les pondrías getters ni setters para que no pueda tocarlos?
De esta manera lo natural sería NO poner setters a los atributos protected de cualquier clase abstracta (de manera que aseguramos su visibilidad) y usar sólo los getters y setters en las propiedades privadas que nos interesen, excluyendo aquellas que puedan resultar peligrosas tocarlas desde otro sitio.
De todas maneras, por norma pon siempre los getter y setter (El único que se salva es si programas en Android; en donde está desaconsejado el uso de getter o setter para ahorro de tiempo de proceso, batería, etc; es la única excepción y aún así, muchos programadores los siguen usando).
Una pregunta, por que existen dos constructores en cada una de las clases? No entiendo por que uno esta vacio y el otro no.
Gracias!
En este caso he puesto dos constructores uno sin atributos y otro con atributos para poder inicializar el objeto con el propio constructor sin necesidad de crearme un objeto y luego tener que ir llamando a cada uno de los métodos «setter» para asignar un valor a los atributos, es simplemente por eso por lo que he hecho dos constructores para escribir algo menos de código en definitiva.
SL2
Los felicito por su blog hay muchas cosas en Java que me parecen excelentes.
Gracias.