Herencia

Inicio
En este tema se tratarán todos los conceptos relacionados con la herencia: superclase, subclase, polimorfismo, clases abstractas, interfaces, etc. Se aportará conocimiento en forma de texto libre sobre los conceptos vistos en clase. Se pueden incluir pequeños fragmentos de código para simplificar alguna explicación, nunca un programa completo. Este tema estará abierto hasta el 24 de febrero.

Definición
La herencia es una parte de Java muy importante que nos ayudará a saber reutilizar códigos generales. Esta nos da la oportunidad de crear una clase nueva cuyo comportamiento es similar a otra, pero especificando más el comportamiento de esta nueva. Ejemplo típico es:

Como vemos en el esquema anterior, tanto los triángulos como los cuadrados podrían agruparse en una superclase llamada Figura que contendría las características comunes a ambas subclases. Matemáticamente se explica con grafos o con relaciones de pertenencia de la forma: Triángulo ⊂ Figura y Cuadrado⊂Figura.

A la hora de hacer una herencia de una clase padre (superclase) a una clase hijo (subclase), la subclase sólo hereda los atributos y los métodos de la superclase, los constructores no, debido a que son específicos de cada clase.

Los métodos de la clase padre por tanto serán aplicables en la clase hija pero NO a la inversa. Detalle que hay que tener en cuenta, por ejemplo, a la hora de generar arrays. Un array de figuras podría incluir tanto cuadrados como triángulos, a la inversa no funcionaría, y por tanto solo serían aplicables los métodos comunes entre ellos, es decir, los de la clase padre. En una subclase si queremos que una función como getInfo devuleva también los atributos específicos de la subclase habría que redefinir el método en la subclase. = =

**Crear Subclases en JAVA**
Para crear una subclase en JAVA se utiliza el código: public class A extends B {}, siendo B la superclase. JAVA automáticamente recibe los atributos y métodos de la clase B, por lo que se puede acceder a ellos desde A. Sin embargo, en ciertas ocasiones, puede que necesitemos usar los métodos de la superclase; este puede ser el caso del getInfo, para ello solo hace falta escribir super.getInfo y accederá al método de la super clase. Este caso se da cuando queremos implementar un método de la superclase añadiendo además nueva información. Un ejemplo de este código sería:

String getInfo {  return super.getInfo+this.portatil; }

Lo único que no se hereda de la superclase son los constructores, los cuales tienen que volver a ser creados para poder ser utilizados. En su defecto se empleará el constructor por defecto en la subclase.Hace falta definir en dicho constructor todos los atributos de la superclase, aunque se hace referencia a ellos gracias a los setters (por ejemplo: this.setNombre(nombre)) de modo que si hay que hacer modificaciones en el método, se acuda a la superclase y no a la nueva subclase definida.

MesaPortatil(String nombre, String material, String portatil) {  this.setAlumno(alumno);  this.setMaterial(material);  this.setPortatil(portatil); }

Como se observa no es necesario llamar a los atributos de la superclase con super.metodo, ya que al ser definidos como argumentos y habiendo extendido la clase, usando "this" el compilador sabe a qué atributo se refiere.

**¿Es útil la herencia?**
Hemos estado hablando en la sesión de clase de hoy de la utilidad de la herencia en la POO y más particularmente de la utilidad de los interfaces. Mucha gente se ha quedado extrañada argumentando que la función del interface se puede hacer desde la superclase ya que las subclases heredan sus métodos (lo que en teoría es la misma función que el interface). No obstante, el problema principal que se plantea es el siguiente: si tengo diferentes subclases que provienen de distintas superclases, ¿tengo que definir un array para cada conjunto de subclases que pertenezcan a una superclase? De esta manera, no es posible relacionar diferentes objetos provenientes de diferentes "padres". He aquí, el punto donde quiero poner en relevancia el uso del interface, ya que te permite la creación de un solo array que permite unificar todos los objetos proveninetes o no de una misma superclase.

Poniendo un ejemplo, se puede ver con más claridad esta explicación: Imaginaos la construcción de un software que posee como superclases: vehículos, máquinas industriales,ordenadores... Estas superclases podrían poseer distintas subclases como: vehículos --> coches, motos, camiones... máquinas industriales --> soldadores, empaquetadoras... electrodomésticos --> lavavajillas, lavadora, tostadora... ordenadores --> PC, portatiles... Estas superclases no tienen sentido en sí mismas, un vehículo no es algo que podamos copiar de la realidad luego no podríamos crear un objeto vehículo, tendríamos que especificar qué tipo de vehículo es (coche, moto, ...). Por ello los interfaces solo implementan métodos abstractos comunes a muchos objetos, no pueden tener ni atributos ni contructores.

Si yo quisiera hacer un estudio acerca del rendimiento actual de la tecnología para sacar conclusiones por ejemplo, sería muy útil un interface que implantase el método abstracto rendimiento; (este método por ejemplo devuelve datos, gráficas...) para de esta manera poder crear un array formado por diferentes objetos que aparantemente no tienen nada que ver entre ellos, para que de esta manera poder llamar a cada rendimiento{} de cada objeto para comparar y analizar las diferentes tecnologías. El interface aquí tendría una utilidad enorme, ya que imaginaos que tendríamos que crear un array para cada superclase y jugar con cada superclase para comprobar su rendimiento. Sería además de un coñazo algo poco útil porque el número de familias puede ser grandísimo

**Clases Abstractas**
Las clases abstractas son un tipo especial de superclase que no permiten definir ocurrencias concretas. Únicamente se pueden definir atributos y métodos (al menos uno abstracto) heredables por subclases que extiendan su comportamiento particularizando la abstracción anterior. Es decir, una clase abstracta es de utilidad para subir un nivel más en la escala de abstracción de la realidad a implementar (objetos). Estos métodos abstractos. se diferencian de los normales en que no tienen nada dentro, simplemente se declara como metodo; denotando su utilidad (pintar;) pero no la forma concreta de cómo lo hace (colorear hasta el borde...). La declaración de uno de estos métodos obligará a implementar en la subclase un método con el mismo nombre definiendo su comportamiento o no. Solo de esta manera se podrá acceder a los atributos de la subclase.

**Arrays de Interfaces**
Lo interesante de poder crear arrays de interfaces es el hecho de poder unir en una sola lista objetos "totalmente" distintos (siempre podemos abstraernos y encontrar nexo de unión entre ellos) con ciertas características comunes, normalmente métodos (pintar, visitable, getInfo...) [atributos en interfaces serían ctes...]. Ademas, a partir de dicha lista, podriamos copiar a otra lista todos los elementos que pertenezcan a una misma clase para poder trabajar con los metodos especificos a ese objeto. De la misma forma, seria interesante implementar el metodo getInfo en los interfaces para poder obtener toda la informacion de los objetos del array de inferfaz ademas del metodo especifico de la clase abstracta.

Herencia Múltiple en JAVA
JAVA se diferencia de otros lenguajes de programación(como C++) en que la herencia sólo se puede realizar a partir de una clase. ¿Qué podemos hacer si queremos que nuestra subclase herede métodos que no están definidos en la superclase? Para ello tenemos las interfaces. Podemos declarar tantas interfaces como nos sea útil a la hora de realizar nuestro programa.En estas interfaces son declarados los métodos abstractos que queramos que sean heredados.Por lo tanto,las subclases pueden heredar de múltiples interfaces y de una única superclase.

**La clase object**
En Java existe una superclase, o clase padre, común a todas las clases declaradas por el programador por defecto. Es decir, todas las clases derivan, de un modo directo o indirecto de ella. Esta es la denominada clase __object__. La clase object aporta una serie de funciones estándar que son comunes a todas las clases (salvo que el programador altere intencionadamente este comportamiento). Un ejemplo de método heredado por defecto de la clase object es equals. Éste se utiliza para comparar, en valor, si dos objetos son iguales. Se trata por tanto de comportamientos tan genéricos que pueden (y normalmente deben) estar presentes en cualquier objeto. Si se declara una clase como heredera de otra (... extends ...), ésta nueva clase sera heredera indirectamente de la clase object. Si se declarase una clase sin hacer referencia a su herencia, automáticamente el compilador añade "extends object" en su declaración, de modo que pasa a ser heredera directa de la clase object.

**Ventajas y desventajas de la herencia**
A la hora de utilizar la herencia podemos encontrar algunas ventajas y desventajas importantes: __Ventajas__: - Gracias a la herencia podemos utilizar los mismos atributos y métodos en diferentes subclases. - El código se reutiliza, se escribe en la clase padre y se hereda a las subclases, es decir, se evita código redundante. - Nos permite organizar las clases y que haya una jerarquía en los objetos. __Desventajas__: - Al hacer un cambio en la clase padre de los atributos y métodos, afecta a las subclases (aunque hay veces que nos conviene) - No soporta la herencia múltiple, es decir, que una subclase o un grupo de subclases solo pueden tener una única clase padre.