Curso de Java. Clases Abstractas e Interfaces.
Una duda muy común entre programadores noveles, es el uso y diferencia entre las clases abstractas y los interfaces. En esta página explicaremos que es cada una e intentaremos despejar las dudas existentes al respecto.
Clases abstractas.
Una clase abstracta es una clase de la cual no se pueden crear objetos. Normalmente contiene métodos abstractos (Encabezamientos de métodos, no tienen el código del cuerpo implementado) y se pueden crear referencias a las que posteriormente poder asignar otros objetos de las subclases.
Para declarar una clase como abstracta debemos utilizar la palabra reservada abstract. Y en el cuerpo de la misma podemos tener miembros de la clase de distinto tipo y métodos tanto abstractos como implementados en la misma clase.
Estructura Básica de una clase Abstracta.
Una clase abstracta puede tener una estructura similar a esta:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public abstract class ClaseAbstracta { // declaraciones de miembros de la clase int num; Date fecha; // declaración e implementación de métodos normales void metodo1(){ System.out.println("Hola compañeros"); } //Declaración de métodos Abstractos abstract void metodo2(); abstract void metodo3(); } |
Como podemos apreciar en la imagen anterior una clase abstracta no diferencia en mucho de una clase normal, salvo por incluir los métodos abstractos.
Heredar de una clase Abstracta.
Heredar de una clase Abstracta, es decir crear otra clase que extienda a la clase abstracta se realiza de la forma habitual, utilizando la palabra reservada extends, pero hay que tener presente algo importante, se deben implementar en la clase que hereda o hija los métodos abstractos de la clase abstracta, de lo contrario esta clase hija tendría que ser abstracta también e incluir la palabra reservada abstract.
Conforme a lo anterior el código para crear una clase que herede de otra clase abstracta bien podría ser así:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public abstract class ClaseAbstracta { // declaraciones de miembros de la clase int num; Date fecha; // declaración e implementación de métodos normales void metodo1(){ System.out.println("Hola compañeros"); } //Declaración de métodos Abstractos abstract void metodo2(); abstract void metodo3(); } public class Clase1 extends ClaseAbstracta { // declaraciones de miembros de la clase pueden ser otros cualquiera String nombre; int edad; // declaración e implementación de otros métodos cualquiera void metodoDespedida(){ System.out.println("Adios amigos"); } //Implementación de los métodos Abstractos abstract void metodo2(){ System.out.println("¿Como te llamas y cual es tu edad?"); nombre=CEntDatos.cadena; edad=CEntDatos.entero; } abstract void metodo3(){ System.out.println("Tienes "+edad+" años, Adios "+nombre); } } |
Vemos que la clase abstracta tiene dos métodos abstractos, con lo cual cualquier clase que herede de ella está obligada a implementar ambos métodos. En caso de implementar solo uno o ninguno la clase habría que hacerla abstracta también.
Por otro lado si en una clase normal queremos tener un método abstracto la clase hay que transformarla en abstracta también.
Interfaces.
Una interfaz en Java tiene similitud con cualquier otra Clase de instancias, pero con importantes diferencias:
- No son instanciables, es decir, no se pueden crear objetos de un interfaz.
- En su cuerpo solo puede haber declaraciones de constantes, encabezados de métodos, métodos estáticos y métodos determinados.
- Salvo los Métodos estáticos y determinados el resto de métodos se deben implementar obligatoriamente por otras clases.
- Una misma clase puede implementar uno o varios interfaces a diferencia de la herencia entre clases que solo se permite a una Clase hija heredar de otra clase o superclase.
- Se permite que un interfaz extienda o herede de uno o varios interfaces.
Estructura básica de un interfaz
1 2 3 4 5 6 7 8 9 10 11 | public interface Calcula { // Declaracion de constantes, son public static final public static final double PI=3.1416; // Encabezamiento de métodos int areaCirculo(int radio); int longCircunferencia(int radio); // Declaracion e implementación de métodos estáticos y determinados } |
Como vemos, para declarar una interfaz añadimos la palabra reservada interface. Las variables o atributos miembro de la clase se definen como constantes public static final, por defecto java ya los declara así, por tanto podemos ahorrarnos el escribirlo. Los métodos que solo declaran las cabeceras (¡Ojo! Omitir corchetes) son abstractos y deben ser implementados por las clases que utilicen o implementen dicho interfaz. Aparte de los métodos abstractos solo se permiten implementar en un interfaz métodos estáticos o determinados, que por defecto también son públicos por tanto podemos omitir el modificador también.
Interface heredando de varios interfaces.
Una interfaz puede extender otras interfaces, separando cada interfaz con comas de esta manera:
1 2 3 4 | public interface Calcula extends interf1, interf2, etc{ // Cuerpo del interface } |
Clases que implementan un Interfaz.
Una clase puede implementar o utilizar un interfaz o varios usando la palabra reservada implements quedando la declaración asi:
1 2 3 4 5 6 | public class Clase1 implements Calcula { //Atributos y métodos de clase // Implementación de métodos abstractos del interface } |
A diferencia de la herencia entre clases, una clase cualquiera puede implementar varios interfaces, mientras implemente los métodos abstractos de cada interfaz:
1 2 3 4 5 6 7 8 | public class Clase1 implements Calcula, Interf1, Interf2 { //Atributos y métodos de clase // Implementación de métodos abstractos de Calcula // Implementación de métodos abstractos de interf1 // Implementación de métodos abstractos de interf2 } |
Clase que hereda de otra clase e implementa una o varias interfaces.
Una clase puede heredar de otra clase y a su vez implementar uno o varios interfaces. En la declaración se antepone siempre la herencia «extends» y después la llamada al interfaz o interfaces «implements»:
1 2 3 4 5 6 7 8 9 10 11 | public class Clase1 extends Clase2 implements Interf1, Interf2, etc { //Atributos y métodos heredados de Clase2 si procede utilizar //Atributos y Métodos de Clase1 // Implementación de métodos abstractos de interf1 // Implementación de métodos abstractos de interf2 //etc } |
Ejemplo funcional de uso de un interface.
En la sección de código mi web podeis encontrar un ejemplo sencillo del uso de un interface. Tan solo es un primer acercamiento a los interfaces y no muestra todo el potencial que podemos sacar a los mismos.
Asignar objetos a un Interface.
Ya dijimos anteriormente que no es posible crear objetos o instancias de un Interfaz, pero si es posible crear variables de tipo interfaz y posteriormente asignar a dichas variables algún objeto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public interface Calcula{ // Cuerpo del interface } public class Clase1 implements Calcula { //Atributos y métodos de clase // Implementación de métodos abstractos de Calcula } public class EjemploInterfaz { public static void main(String[] args) { //Variable tipo Calcula (interfaz) Calcula interf1; //Instancia u objeto de Clase1 Clase1 C1=new Clase1(); //Asignamos el objeto a la variable del interfaz interf1 = C1; } } |
Sin embargo hay dos cosas importantes que debemos saber y tener siempre presente:
- Los objetos asignados a una referencia de un interfaz han de ser obligatoriamente instancias de clases que implementen dicho interfaz.
- Solo se tiene acceso desde una variable de tipo interfaz a los métodos propios del interfaz, nunca a métodos de otras clases.
Buenas prácticas declarando Interfaces.
A la hora de crear o declarar Interfaces conviene asegurarse y pensar que contendrá el mismo para evitar trastornos posteriores que puedan surgir del olvido de algún método, sobre todo si este es abstracto. La razón es muy sencilla, un interfaz puede tener métodos implementados en una, dos o cientos de clases. Al incluir un método nuevo en un interfaz ya muy utilizado por el resto de clases, nos veríamos obligados a implementar dicho método en todas ellas y dependiendo del tipo de programa o aplicación donde se de uso, esta tarea puede ser difícil o imposible.
Si nos viesemos obligados a modificar la implementación o algún método de un interfaz, lo más razonable seria incluir dicho método en un nuevo interfaz que extienda al interfaz antiguo, de esa manera no perjudicaremos las clases que implementan el interfaz de versión antigua. Cuando la modificación del interfaz afecta a uno o unos pocos métodos, otra buena solución seria utilizar Métodos predeterminados o default e implementados íntegramente en el Interfaz. Para crear un método predeterminado anteponemos el modificador o la palabra reservada default, de la siguiente manera:
1 2 3 | default Tipo metodoDeterminado(Arg1, Arg2..) { return ; } |
Donde «Tipo» es el tipo de retorno, «metodoDeterminado» es el nombre del método e incluiríamos los argumentos si los hubiese.
Diferencia y Semejanza entre Clases Abstractas e Interfaces.
Semejanza:
- No se pueden crear objetos ni en Clases Abstractas, ni en Interfaces.
Diferencia:
- En las clases abstractas, se pueden declarar atributos diferentes, no necesariamente u obligatoriamente han de ser estáticos y finales.
- Las clases abstractas pueden declarar otro tipo de métodos aparte de «abstractos», «estáticos» y «default», como protegidos y privados.
- Las clases abstractas solo pueden heredar de una clase a diferencia de los interfaces que pueden heredar de varios Interfaces.
Conclusiones finales.
El uso de Clases Abstractas es adecuado en programas con una jerarquía de herencia donde se compartan muchos métodos comunes. Es decir estén relacionadas en un contexto global.
El uso de interfaces quizás es más adecuado en implementaciones de clases con poca o nula relación. Por supuesto, cuando se precisa que una clase herede de varias superclases, lo indicado es hacer uso de interfaces.
Desarrollaremos más este tema cuando hablemos de polimorfismo y selección dinámica en tiempo de ejecución de métodos. De momento con lo explicado, nos hacemos una idea de lo que es una clase Abstracta y un Interfaz.
Como siempre digo, cualquier duda, corrección, consejo o palabra de ánimo (jejeje), será muy bienvenida. Si has llegado hasta aquí, te doy las gracias por ello y te animo a dejar un comentario…