Cuando declaremos una interfaz lo que vamos a definir es un contrato, un comportamiento externo con el resto de código, que deben asegurar todos los objetos que implementen esa interfaz (recordad que poníamos el ejemplo de una sierra, cualquier sierra debe tener el método
cortar
, sin embargo también podemos decir que una tijera puede cortar, e incluso el propio papel puede cortarte el dedo XD).Estos objetos que implementan (cumplen el contrato) pueden ser de tipos con ninguna relación como hemos visto con el ejemplo de cortar (sierras, tijeras o incluso papel).
Con las interfaces podemos declarar el comportamiento de tipos completamente distintos
Como deben asegurar un comportamiento, todos los miembros declarados en una interfaz son públicos, con lo que no hace falta añadir modificaciones de acceso pues ya se toma por defecto. Si quisiéramos declarar uno con un acceso más restrictivo obtendremos un error.
Dos de las Interfaces más famosas, en mi opinión, son Collection y Comparable. Éstas interfaces utilizan genéricos y para sacarles todo el partido hay que conocer los Tipos Variables, así que iremos profundizando en ellas poco a poco.
Para entender bien qué es y cómo usar una interfaz voy a crear una interfaz llamada Arrancable, que sólo tenga un método: arrancar(). El código necesario para esto es:
public interface Arrancable {
void arrancar();
}
Vemos que el método arrancar() no tiene cuerpo. Es normal y se escribe de esa forma para declarar que es un método abstracto, es decir, sólo defino qué métodos debe tener, pero no cómo se implementan, ese trabajo se deja para las clases que quieran adherirse a este contrato "Arrancable".Ahora voy a usar nuestra clase Coche para implementar esta interfaz. Implementar una interfaz quiere decir que debe tener una implementación para cada método que falte por definir. Luego veremos porque digo "los que falten". El código para implementarla es:
public class Coche extends VehiculoConRuedas implements Arrancable {
...
@Override
public void arrancar() {
System.out.println("Coche arrancado");
}
}
Declaramos que se implementa una interfaz con implements y el nombre de la interfaz (o varias separando su nombre por comas). Eclipse va a detectar qué métodos faltan por implementar, y me va a ayudar a escribir el código. Aceptamos la opción "Add all unimplementd methods" que nos da el error o los creamos nosotros. Para terminar nos queda rellenar el cuerpo y habremos acabado.Como se puede ver también aparece la anotación @Override como ya vimos en herencia.
Palabra reservada default
Existe una palabra reservada llamada default que nos va a servir para declarar una implementación por defecto para un método de interfaz.Por otra parte, nuestra clase que implemente una interfaz podría heredar de una clase abstracta que también la implementase parcialmente (recordamos que las clases abstractas no necesitan tener implementados todos sus métodos)
Por estos dos motivos decía que implementar la interfaz es declarar todos los métodos que faltan, ya que podríamos tener parte del trabajo hecho o incluso todo (la interfaz Serializable no necesita que se implemente ningún método)
Te propongo que practiques lo siguiente:
- Comenta la implementación de arrancar() en Coche e implementa Arrancable en VehiculoConRuedas con un código distinto al comentado en Coche para arrancar(). Observa que ya no se pide implementar ningún método en Coche.
- Descomenta arrancar() en Coche, coméntala en VehiculoConRuedas y añade una implementación default distinta de las anteriores en Arrancable. Ahora créate un objeto Coche y un objeto VehiculoConRuedas e invoca en los dos el método arrancar(). Deben salirte resultados distintos entre ellos ¿Sabes por qué?
Estos ejemplos son muy básicos y podrías pensar que esto mismo lo lograrías definiendo un método arrancar() directamente en VehiculoConRuedas. Tienes razón. Por eso en la siguiente sesión vamos a ver LO IMPORTANTE de las interfaces haciendo que clases completamente distintas acepten el contrato que marca una interfaz.
No hay comentarios:
Publicar un comentario