Interfaces en Java
Uno de los pilares de la Programación Orientada a Objeto es la abstracción, y una forma de lograr este principio es creando y/o utilizando interfaces.

Créditos de fotografía de portada: Davide Boscolo en Unsplash
¿Qué es una interfaz?
Técnicamente una interfaz es un tipo abstracto que se utiliza para especificar el comportamiento de una clase. Piensa en una interfaz como en una plantilla para crear clases. Para más sobre la definición del concepto de interfaz, puedes leer en el blog de Arkaitz Garro, en donde explica de forma brillante este concepto. También si lo deseas, puedes darte una vuelta por
Una interfaz define une una lista de tareas que una clase debe implementar. De ahí que se emplea la abstracción, puesto que se define la idea “abstracta” o el cascarón de una clase, pero sin implementaciones específicas.
En este sentido, puede parecerse mucho a una clase abstracta, pero existen diferencias importantes entre ellas, como te muestro en la figura 1.

¿Por qué crear interfaces?
En lo particular considero que es más útil crear y/o utilizar interfaces que clases abstractas. ¿Por qué? Bueno, para empezar, si ya han estudiado algo de la documentación de java, verán que tiene bastantes interfaces que permitirán utilizar constantes y métodos a todas las clases que las implementen, y además nos permitirá la comodidad de implementar éstos métodos en la forma en la que mejor se adapte a las necesidades de programación.
Quizá si eres muy nuev@ programando esto no te parecerá como una definición de texto muy clara. Pero toma en cuenta que el concepto de interfaz lo vas a entender y manejar mejor conforme vayas avanzando en tu formación como programador(a).
Mientras tanto, considera las siguientes ventajas de usar interfaces:
- Permiten declarar constantes que estarán disponibles para todas las clases que implementen la interfaz. Imagina que requieres declarar diversas constantes de física, química o matemáticas, ¡sólo deberías definirlas una vez en la interfaz!
- Aunque parezca más complicado usar interfaces, en realidad estarás organizando mejor tu código. Piensa que estás programando en equipo y que no todos son tan organizados, por lo que les defines desde un principio: estos son los métodos que usaremos, sus argumentos y su tipo de regreso. Es cierto que puedes establecer estas reglas, pero sabemos que no tod@s cumplirán con las normas. Entonces les dices simplemente: todas sus clases deben implementar ésta interfaz. Ahora sí, no tienen de otra más que apegarse al “contrato” que estás creando al definir las firmas de los métodos en la interfaz.
- Aunque algunos lenguajes OO permiten la herencia múltiple, sabemos que Java no lo hace así. De tal manera que, si en un problema necesitas esta funcionalidad, una forma de resolverlo es a través de interfaces. Esto también te permitirá establecer relaciones de clase en clases no relacionadas.
Creación de una interfaz.
En la figura 2 te muestro la forma general para construir una interfaz:

Toma en cuenta lo siguiente:
- Una interfaz siempre utiliza el modificador de acceso public. Las interfaces no pueden ser private, protected o final. Recuerda que una interfaz es básicamente una colección de encabezados de métodos que serán implementados por la clase que la use, por lo que si se declarara como privada o protected ninguna clase podría tener acceso a éstos métodos. Por otro lado, si se definiera una interfaz como final, esto implicaría que no se podrían implementar los métodos.
- Al igual que las clases, una interfaz debe ser definida en un archivo con el mismo nombre de la interfaz que tenga la extensión .java. Para el nombre de la interfaz, las reglas para los identificadores de clase también son aplicables.
- Recuerda que sólo debes escribir la firma de los métodos, y que éstos implícitamente son public y abstract, por lo que estas palabras se pueden omitir. Al declararse sólo el encabezado del método, recuerda terminar tu declaración con ;
- Las constantes declaradas en una interfaz son implícitamente public, static y final, por lo que no es necesario utilizar estos modificadores en la declaración. Lo que sí debes hacer, como siempre que se declaran constantes, es inicializarlas.
Uso de una interfaz.
En la figura 3 te muestro la forma general para construir una clase que implementará una interfaz.

Algunas consideraciones a tomar en cuenta:
- La palabra clave implements obliga a la clase a implementar todos y cada uno de los métodos declarados en la interfaz. Por ello, aunque consideres que no utilizarás un método, debes implementarlo en tu clase. Puedes dejarlo vacío y nunca llamarlo en tu programa, pero debe estar presente o de lo contrario tendrás un error de sintaxis (observa la figura 4).
- Las cabeceras de los métodos declarados en la interfaz deben aparecer en la clase tal y como fueron declarados en la interfaz, con sus tipos de método y argumentos correspondientes. Cualquier cambio de tipo de método, tipo de argumento y/o número de argumentos es suficiente para que sea considerado como un método diferente, el cual, o no se encuentra declarado en la interfaz, o no se encuentra definido en la clase; por lo que esta situación generará un error.
- La clase que implementa tiene el derecho de utilizar las constantes declaradas en la interfaz, como si hubieran sido declaradas en la clase.
- Si una clase no utiliza la palabra implements en su declaración, pero requiere utilizar las constantes de la interfaz, debe anteponer el nombre de ésta antes de la constante de la siguiente forma:
NombreInterfaz.NombreConstante

Explicaré con más detalle lo que está sucediendo en la figura 4. La clase Ulam está implementando a la interfaz Series. Dicho en una forma menos técnica, Ulam utilizará lo declarado en Series. Pero como puedes observar, no está escribiendo el código para ninguno de los métodos que seguramente se encuentran en la interfaz. Y esa es la causa del error.
Ejemplo
A continuación, te muestro un proyecto de Java en el que he utilizado una interfaz y una clase que la implementa.
Para este ejemplo, voy a considerar el problema de crear series numéricas. La serie más simple que puede existir es la siguiente:
1, 2, 3, 4, 5, etc.
En donde el siguiente número es el resultado de sumarle 1 al anterior.
Obviamente existen series mucho más complejas, pero a efecto de usar una interfaz, voy a representar a cualquier serie con una abstracción muy sencilla y simple:
numeroInicial, numeroSiguiente, numeroSiguiente , …
Teniendo esto en mente, he construido una interfaz que me servirá para diversas series matemáticas:
/* * d.CodinGames.com * Interfaz Series, a ser implementada por la clase que lo requiera * @author Gaby Nieva - dCodinGames.com */ public interface Series { // declaración de constantes public final int INCREMENTO1 = 1; public final int INCREMENTO2 = 2; //definición de firmas de métodos public int siguiente(int numActual); public int anterior(int numActual); public boolean revisarFinSerie(int numActual); }
Como puedes observar, la interfaz Series tiene dos constantes, que pueden ser utilizadas por la clase que las implemente. Pero además tiene tres métodos definidos: siguiente, anterior y revisarFinSerie, los cuales considero que son básicos para generar cualquier serie numérica.
Ahora te muestro la clase SerieSimple que implementa la interfaz. En esta clase, los tres métodos se programan con la forma específica en que la serie numérica requiere hacer sus incrementos. Además, también puede utilizar las constantes INCREMENTO1 e INCREMENTO2.
/* * d.CodinGames.com * Clase SerieSimple, implementa la interfaz Serie * @author Gaby Nieva - dCodinGames.com */ public class SerieSimple implements Series{ private int numFinal; private boolean tipoIncremento; public void setNumFinal(int nfinal){ this.numFinal = nfinal; } public void setTipoIncremento(boolean incremento){ this.tipoIncremento = incremento; } @Override public int siguiente(int numActual){ if (tipoIncremento) return numActual+INCREMENTO1; else return numActual+INCREMENTO2; } @Override public int anterior(int numActual){ return 0; } @Override public boolean revisarFinSerie(int numActual){ return numActual == numFinal; } }
También he creado una clase que sirve para una serie más compleja. Quizá recuerdes la serie de Ulam, clásica serie matemática muy utilizada en la programación para aprender el uso de ciclos. En esta ocasión la he utilizado para mostrar la versatilidad de utilizar interfaces.
/* * d.CodinGames.com * Clase Ulam, implementa la interfaz Serie * @author Gaby Nieva - dCodinGames.com */ public class Ulam implements Series{ @Override public int siguiente(int numActual){ if (numActual % 2 == 0 ) return numActual/2; else return numActual*3+1; } @Override public int anterior(int numActual){ return 0; } @Override public boolean revisarFinSerie(int numActual){ return numActual == 1; } }
Como puedes observar, los métodos siguiente() y revisarFinSerie() son completamente diferentes en las clases Ulam y SerieSimple, pero tienen en común la firma de los métodos, las cuales son iguales a las que se declararon en la interfaz. En ambas clases, el método anterior() no fue utilizado, pero sí debió implementarse por la regla del uso de interfaces. Alguien podría opinar que sería mejor quitarlo, pero si después requiero programar la serie de Fibonacci, en la cual se requiere llevar un registro del número anterior, esta interfaz me podría ayudar con esa implementación. De todos modos, el objetivo era que vieras que si una interfaz tiene un método que no vas a utilizar, lo tienes que implementar, aunque quizá no haga nada importante.
Por último, la verdadera utilidad de las interfaces y las clases que implementan interfaces se puede observar en el siguiente programa que utiliza las clases SerieSimple y Ulam:
public static void main(String[] args) throws IOException { int numero; //numero inicial de las series SerieSimple serie = new SerieSimple(); System.out.println("Series numéricas - dCodinGames.com"); serie.setTipoIncremento(true); //incremento de 1 en 1 serie.setNumFinal(10); //La serie termina en 10 numero = 0; //valor inicial de la serie System.out.println("Serie numérica con incremento de 1 en 1: "); System.out.print(numero); do{ numero = serie.siguiente(numero); System.out.print(" , " + numero); }while (!serie.revisarFinSerie(numero)); System.out.println(); System.out.println("Serie numérica con incremento de 2 en 2: "); numero = 0; //valor inicial de la serie serie.setTipoIncremento(false); //incremento de 2 en 2 System.out.print(numero); do{ numero = serie.siguiente(numero); System.out.print(" , " + numero); }while (!serie.revisarFinSerie(numero)); System.out.println(); Ulam serieUlam = new Ulam(); numero = 15; System.out.println("Serie de Ulam: "); System.out.print(numero); do{ numero = serieUlam.siguiente(numero); System.out.print(" , " + numero); }while (!serieUlam.revisarFinSerie(numero)); System.out.println(); }
Las líneas de código que generan la serie 0, 1, 2, 3,.. , la serie 0, 2, 4, 6, …, y la serie de Ulam son exactamente iguales, ocultando de esa forma los detalles de la implementación y ofreciendo a los usuarios de las clases un nivel de abstracción más simple de utilizar y probablemente comprender. En la figura 5 te dejo una captura de pantalla de la ejecución de este programa:


Si consideras que este artículo te fue útil, te agradecería que lo compartas en tus redes sociales. Y si tienes alguna duda sobre el tema, déjame un comentario y/o pregunta.
Puedes descargar el código fuente completo utilizado en éste artículo en este enlace.
¡Hasta la próxima!
Leído Profesora!
Daniel Fernandez Guarneros 4A DSM
Leído
Joanna Monserrat Espinoza Sánchez 4A TIADSM
Leído
Josue Muñoz Avila 4A TI-DSM