Curso básico de Java - #17 - POO: Herencia

Buenas,
En este tema volvemos a la POO viendo una característica muy importante de esta; la herencia.


Teoria

  • La herencia permite que una clase herede (tenga como suyos) los métodos y atributos de otra.
  • Las herencias son jerárquicas; Puede haber una cadena de herencias, y cuanto mas “debajo” este la clase mas elementos heredara.
  • Una clase solo puede heredar de otra. En Java no hay herencia múltiple como tal, aunque, si que existe una forma de emularlo; con interface la cual no creo que lleguemos a ver en este curso.
  • De hecho todas las clases que creamos y que ya están definidas, heredan de la clase object, lo cual nos permite usar métodos como toString(), equals(), getClass(), …
  • Podemos definir la relación (de herencia) entre dos clases como superclase (la clase “Padre/madre”) y la subclase (la clase hijo/a).
  • Por defecto, las clases pueden ser superclase, es decir, se puede heredar de ellas. Pero también es posible bloquear esta característica declarando la clase como final.
    Ejemplo de esto es la clase String, de la cual no podemos heredar.

¿Para que nos sirve la herencia?
Si necesitamos crear una clase similar a otra, pero con algún cambio, o si queremos crear varias clases parecidas, que seguramente compartan algunos atributos y métodos, pero, necesariamente tienen que ser diferentes, podemos crear una clase que recoja, por así decirlo, lo básico de todas las clases. Esto nos permite reutilizar y ordenar adecuadamente el código así como integrar funciones ya escritas en nuestro programa de manera eficiente.


Sintaxis
Simplemente debemos añadir la clausula extends [clase] al final de la declaración de una clase.

Sintaxis
public class [nombre] extends [superclase]
{
    // Definición de la clase.
}
Ejemplo
public class Contable extends Trabajador
{
    // Definición de la clase.
}

Override o sobrescribir
Hemos dicho que la una clase heredar de otra, la subclase pasa a tener los mismos métodos y atributos que la superclase mas los suyos propios.

¿Pero que pasa si queremos que el bloque de código de x método sea diferente?
En ese caso podemos sobrescribir el método original:

No hay que hacer nada complicado, solo hay que declarar el método igual que el de la superclase y definirlo como se quiera.
Esto automáticamente remplazara el método de la superclase cuando se llame desde la subclase (o un objeto suyo) así como de las clases que hereden de la subclase.

Ejemplo

El método toString siempre es interesante personalizarlo:

@Override
public String toString()
{
	return "Oficinista de la oficina: " + numero_oficina + ", con el cargo de: " + cargo + ".";
}

la etiqueta @Override no es necesaria, pero no esta de mas. Eclipse la pone automáticamente.


super()
Cuando la superclase cuenta con uno o mas métodos constructores, debemos llamarlo en el constructor de la subclase.

Esto se hace mediante el método super() y es necesario porque cuando se instancia un objeto, también se instancia la superclase del mismo.

Ejemplo
public class Trabajador
{
    public Trabajador (String n)
    {

}

public class Contable extends Trabajador
{
    // Definición de la clase.
}

Por ejemplo,
Queremos hacer varias clases para representar figuras; Rectángulos , Triángulos, Círculos… Todas tendrán sus diferencias; Los rectángulos se medirán con dos lados, los círculos con el ratio… Pero todas compartirán atributos como el color y métodos como devolver el perímetro, el área… Estos elementos comunes pueden ser parte de la clase Figura de la cual heredaran el resto.

Figura.java
public class Figura
{
	public String color;
	
	public Figura (String c)
	{
		color = c;
	}
	
	public String getColor()
	{
		return color;
	}
	
	public double getArea()
	{
		return 0;
	}
	
	public double getPerimetro()
	{
		return 0;
	}
	
	public String toString()
	{
		return "Color: " + color + ".";
	}
}

En vez de devolver 0, podríamos usar abstracción, pero no creo que nos de para verlo en este curso básico.

Circulo.java
public class Circulo extends Figura
{
	private double radio;
	
	public Circulo(String c, double r)
	{
		super(c);
		radio = r;
	}
	
	@Override
	public double getArea()
	{
		return Math.PI * Math.pow(radio,2); // π x radio². Ya veremos la clase Math mas adelante.
	}
	
	@Override
	public double getPerimetro()
	{
		return 2 * Math.PI * radio; // 2 x π x radio. Ya veremos la clase Math mas adelante.
	}
	
	@Override
	public String toString()
	{
		return "Color: " + color + ", radio: " + radio + ".";
	}	
}
Rectangulo.java
public class Rectangulo extends Figura
{
	private double altura, base;
	
	public Rectangulo(String c, double altura, double base)
	{
		super(c);
		this.altura = altura;
		this.base = base;
	}
	
	@Override
	public double getArea()
	{
		return altura * base;
	}
	
	@Override
	public double getPerimetro()
	{
		return 2 * (altura + base);
	}
	
	@Override
	public String toString()
	{
		return "Color: " + color + ", altura: " + altura + ", base: " + base +".";
	}
}
Triangulo.java
public class Triangulo extends Figura
{
	private double altura, a, b, c;
	String color = "Azul.";
	
	public Triangulo(String color, double altura, double a, double b, double c)
	{
		super(color);
		this.altura = altura;
		this.a = a;
		this.b = b;
		this.c = c;
	}
	
	@Override
	public double getArea()
	{
		return (a * altura) / 2;
	}
	
	@Override
	public double getPerimetro()
	{
		return a + b + c;
	}
	
	@Override
	public String toString()
	{
		return "Color: " + color + ", altura: " + altura + ", lado 'a': " + a 
				+ ", lado 'b': " + b + ", lado 'c': " + c +".";
	}
}

Aunque ha sido una explicación exprés, creo que la base se entiende. Si queda alguna duda o alguien quiere aportar o citar información extra relacionada, le animo a hacerlo.

En el siguiente tema veremos enum como una estructura de POO en Java.

3 Me gusta

Muchas veces he escuchado por internet que usar herencia es una mala practica.

Cuál sería algún inconveniente de esta?

1 me gusta

Buenas,
Sinceramente no estaba enterado de esta corriente, tras investigar un poco he visto algunos posts y artículos criticandola.

Yo la verdad es que no estoy de acuerdo con la crítica; muchos alegan que se usa sin sentido, que complica inecesariamente el codigo. La herencia es una “herramienta” o característica de la POO, no es mala, claro, si se usa mal pues obviamente te dará malos resultados.

A mí me parece fundamental conocerla y saber cuándo aplicarla.

Asique si, para mí está bien, y me parece que muchaos de los que la critican están frustrados o no tienen una base muy solida en POO.

Pero bueno, eso es mi opinión, ya lo he dicho muchas veces, no soy ningún experto, solo un estudiante con una base.

3 Me gusta

En efecto, la herencia como herramienta es muy útil y ayuda a reducir muchísimas líneas de codigo en proyectos grandes. Otra de las cualidades es que permite crear una base de código solida sobre la que se montará el proyecto.

El objetivo de la herencia es el mismo que el de las funciones, reducir la redundancia y líneas de código.

1 me gusta