Java Her en CIA Interfaces
Java Her en CIA Interfaces
Java Her en CIA Interfaces
C++ y Eiffel (entre otros) admiten herencia múltiple. Esa flexibilidad deben pagarla en
complejidad o ineficiencia. De forma genérica, el conflicto de la herencia múltiple ocurre
porque una clase derivada puede recibir:
4-1
Laboratorio de Informática II - Interfaces
Vehículo
VehículoTerrestre VehículoAcuatico
¿Qué pasaría si la clase Vehículo tuviera un atributo llamado matrícula? ¿Cuántas matrículas
tendría la clase VehículoAnfibio?
Y además, ¿qué pasaría si Vehículo tuviera un método que fuera implementado de forma
diferente por VehículoTerrestre y por VehículoAcuatico.
Otro ejemplo: supongamos que tenemos la clase Animal, que describe el comportamiento
genérico de todo animal y otra Volador, que describe el comportamiento de todo lo que
vuela. ¿Cómo crearíamos la clase Pájaro? Lógicamente podría ser hija de ambas, pero Java nos
lo prohibe.
Un interface es una clase que describe sólo especificaciones de métodos, sin código ni
atributos, y que sí se pueden indicar como “padres”de una jerarquía múltiple.
Es decir, con los interfaces se obtiene lo bueno de la herencia múltiple, eliminando lo malo.
No se hereda codificación sólo definición de métodos.
Los interfaces actúan, por tanto, como tipos de clases. Se pueden declarar variables que
pertenezcan al tipo del interface, se pueden declarar métodos cuyos argumentos sean del tipo
del interface, asimismo se pueden declarar métodos cuyo retorno sea el tipo de un interface.
4-2
Laboratorio de Informática II - Interfaces
En cualquiera de estos casos, lo que realmente estamos indicando es que cualquier objeto que
implemente ese interfaz puede ocupar el lugar de esa variable, de ese argumento o de ese
valor de retorno; cualquier objeto que pertenezca al tipo del interface y por tanto, cualquier
objeto que cumpla el comportamiento definido en ese interface.
Una clase puede implementar tantos interfaces como desee, pudiendo, por tanto, pertenecer a
tantos tipos de datos diferentes como interfaces implemente. En este sentido, los interfaces
suplen la carencia de herencia múltiple de Java (y además corrigen o evitan todos los
inconvenientes que del uso de ésta se derivan).
El ejemplo anterior define un interface llamado Driveable (capacitado para ser conducido)
compuesto por cuatro métodos. Estos métodos podrían declararse con la cláusula abstract
(por no tener implementación) pero al tratarse de un interface es algo que se sobreentiende
(siempre todos los métodos de un interface van sin implementación) y no se ha indicado. De
la misma forma, todos los métodos de un interface son siempre públicos y tampoco es
necesario indicarlo explícitamente.
Los interfaces definen capacidades, por esta razón, suelen ser nombres adecuados aquellos que
denoten capacidades (ejecutable, actualizable, ordenable, dirigible,...). Toda clase que vaya a
implementar un determinado interface debe indicarlo mediante la cláusula implements, y a
continuación está comprometida a implementar todos y cada uno de los métodos definidos
en el interface.
class Automobile implements Driveable {
...
public boolean startEngine() {
if ( notTooCold )
engineRunning = true;
...
}
4-3
Laboratorio de Informática II - Interfaces
• En cualquier lugar donde se requiera una variable del tipo Driveable podremos situar
un objeto de la clase Automobile.
Cualquier otra clase, como por ejemplo la clase LawnMower, podría también implementar el
interface Driveable. La figura 4.3 muestra esta circunstancia: el interface Driveable está siendo
implementado por dos clases diferentes. Además, sería perfectamente posible que ambas
clases, Automobile y Lawnmower, fuesen descendientes a su vez de cualquier otra clase base
que represente un tipo básico de vehículo. En este supuesto se muestra la ventaja que
proporcionan las interfaces frente a los problemas que ocasiona la herencia múltiple en C++ y
como, en Java, con las interfaces, se puede prescindir completamente de ésta.
4-4
Laboratorio de Informática II - Interfaces
vehicle = auto;
vehicle.startEngine();
vehicle.stopEngine();
...
vehicle = mower;
vehicle.startEngine();
vehicle.stopEngine();
Tanto la clase Automobile como la clase Lawnmower implementan el interfaz Driveable y por
tanto todos sus objetos pueden considerarse, además, del tipo Driveable.
Supongamos que además del interfaz Driveable (definido anteriormente) tenemos un nuevo
interfaz llamado Cloneable (capacidad para ser clonado). Este sería su código:
interface Cloneable {
Object clone();
}
Y supongamos, además, que existe una clase base para todos los tipos de vehículos llamada
Vehicle. Si la clase Automobile quisiese derivar de la clase Vehicle e implementar los interfaces
Driveable y Cloneable, se haría de la siguiente manera:
class Automobile extends Vehicle implements Driveable, Cloneable {
...
public boolean startEngine() {
...
}
4-5
Laboratorio de Informática II - Interfaces
Como puede observarse, la clase Automobile debe implementar los métodos de todos y cada
uno de los interfaces que implemente. En estos momentos los objetos de la clase Automobile
pertenecen a 4 tipos distintos:
• El tipo Automobile
• El tipo Vehicle
• El tipo Driveable
• El tipo Cloneable
Un interfaz no puede especificar que él implementa a otro interfaz porque los interfaces no
incluyen ningún tipo de implementación en sus métodos. Sin embargo, y como ya se ha
comentado anteriormente, los interfaces pueden derivar de otros interfaces. De hecho, un
interfaz puede derivar de tantos interfaces como quiera (¡se permite la herencia múltiple entre
interfaces!), al contrario de lo que ocurre con la herencia entre clases donde sólo de permite
heredar de una sola clase.
Si un interfaz quiere derivar de 2 ó más interfaces, se deben incluir éstos después de la palabra
reservada extends y separados por comas. Este es un ejemplo:
interface DynamicallyScaleable extends Scaleable, SomethingElseable {
...
4-6
Laboratorio de Informática II - Interfaces
El interface Scaleable (escalable) define tres variables de tipo entero: BIG, MEDIUM y SMALL.
Todas las variables que se definan en un interface son implícitamente (y de forma obligatoria)
FINAL y STATIC. Por tanto, no es necesario incluir estas cláusulas en su definición, pero para
mejorar la claridad se recomienda incluirlas.
Una clase que implemente el interface Scaleable tendrá acceso a estas tres variables, aunque
solamente podrá consultar sus valores ya que al ser constantes (FINAL) no podrá modificarlas.
El siguiente código muestra un ejemplo:
class Box implements Scaleable {
4-7
Laboratorio de Informática II - Interfaces
la clase que lo haga quede “marcada” como del tipo Serializable y por tanto, pueda ser
serializada (ser convertida en un flujo de bytes para ser transportada por la red).
4.4.1.- Clases
• Tipo que al extenderlo mediante herencia se obtienen sus mensajes y sus
implementaciones (métodos y atributos).
• Ventaja: Menor codificación al crear nuevos subtipos ya que los mensajes vienen con
sus implementaciones.
4.4.2.- Interfaces
• Tipo que al extenderlo mediante herencia se obtiene solamente mensajes.
• Reglas de diseño:
- Solo se usarán clases abstractas cuando se esté seguro de que los objetos a
manipular no necesitan participar de más tipos y se desee reutilizar las
implementaciones.
Según lo anterior: ¿Cuando se justificaría una clase con todos los métodos
abstractos?
4-8
Laboratorio de Informática II - Interfaces
interface B {
void mensaje3();
void mensaje4();
}
class C implements A, B {
void mensaje1() { System.out.println(“1”); }
void mensaje2() { System.out.println(“2”); }
void mensaje3() { System.out.println(“3”); }
void mensaje4() { System.out.println(“4”); }
}
interface B extends A {
void mensaje2();
}
class C implements B {
void mensaje1() { System.out.println(“1”); }
void mensaje2() { System.out.println(“2”); }
}
interface B extends A {
void mensaje2();
}
4-9
Laboratorio de Informática II - Interfaces
interface C extends A {
void mensaje3();
}
interface D extends B, C {
void mensaje4();
}
class E implements D {
void mensaje1() { System.out.println(“1”); }
void mensaje2() { System.out.println(“2”); }
void mensaje3() { System.out.println(“3”); }
void mensaje4() { System.out.println(“4”); }
}
Las clases que implementen un interface deben aportar el código para todos los métodos. Si
no es así serán a su vez clases abstractas. En el siguiente ejemplo la clase Avion debe ser
declarada abstracta porque no ha proporcionado ninguna implementación para el método
acelerar():
abstract class Avion implements ElementoMecanicoVolador
{
boolean elMotorFunciona;
public void arreglarMecanismos()
{
elMotorFunciona = true;
}
}
Finalmente alguna clase acabará por implementar todos los métodos del interface y será una
clase no abstracta. En el siguiente ejemplo la clase AvionComercial ha implementado el
método acelerar() que la clase abstracta Avion dejó sin implementar y por tanto, es una clase
no abstracta:
class AvionComercial extends Avion
4-10
Laboratorio de Informática II - Interfaces
{
int velocidad;
public void acelerar( int vel )
{
velocidad += vel;
}
}
Existen múltiples algoritmos de ordenación de arrays que podríamos usar, pero el hecho de
que nuestro método vaya a poder ordenar arrays con cualquier tipo de elementos dentro
hace que surjan una serie de cuestiones:
• ¿De qué tipo tendríamos que definir el array si no sabemos el tipo objetos que va a
contener?
• ¿Cómo podemos obtener el valor de cada objeto del array según el cual vamos
realizar la ordenación?
En realidad nos debería dar igual el tipo del que sean los objetos contenidos en el array, lo
único que necesitamos es que todos los objetos posean un método que nos permita consultar
el valor de la magnitud por la que vamos a realizar la ordenación, y para que el método de
ordenación que estamos diseñando pueda usarse de manera genérica, que ese método sea el
mismo para todos los objetos. Para ello podríamos crear un interfaz llamado, por ejemplo,
Ordenable que contenga una única operación getValor():
interface Ordenable{
int getValor();
}
De esta forma, obligaremos a que todas las clases cuyos objetos quieran ser ordenados
mediante nuestro método tengan que implementar el interfaz Ordenable. Cada clase
implementará el método getValor() de la forma que crea más apropiada. Por ejemplo:
public class Persona implements Ordenable{
...
public int getValor() { return estatura; }
}
4-11
Laboratorio de Informática II - Interfaces
La clase Persona considera que la magnitud por la cual quiere ser ordenada debe ser la
estatura e implementa la operación getValor() para que devuelva la estatura de la persona. La
clase Avion considera que debe ser ordenada por su envergadura (alas + cola).
Al definir el interfaz Ordenable hemos definido un nuevo tipo de datos. Ahora el tipo que
debe tener nuestro array está claro: deberá ser un array de objetos de tipo Ordenable.
Ordenable[] array;
Nos da igual qué objetos contenga el array, nuestro método de ordenación lo único que
necesita es que todos los objetos implementen el interfaz Ordenable. Sólo tiene que tener la
garantía de que todos los objetos poseen la operación getValor() que es la que él va a usar, el
resto de características del objeto (ni siquiera la clase a la que pertenece) no le interesan.
Esto mismo se podría haber hecho declarando una clase abstracta Ordenable, haciendo que
todas las clases hereden de ella y sobreescribiendo el método getValor(). Pero, ¿qué pasaría si
la clase Avión o Persona ya estuviesen heredando de otra superclase? o ¿qué pasaría si estas
clases quisiesen participar en otros métodos de ordenación aparte del nuestro? Necesitaríamos
que esas clases heredasen de más de una clase. Java no permite herencia múltiple entre clases y
por tanto, si no fuese por las interfaces no podríamos hacerlo.
4.8.- RESUMEN
Los interfaces, en general, entonces:
4-12
Laboratorio de Informática II - Interfaces
• Pueden definir una jerarquía de interfaces; y en ella se pueden definir padres múltiples
(ya que en esa jerarquía no se hereda más que una lista de definiciones de métodos, sin
código).
• Pueden definirse variables de tipo interface; pero lo que querrá decir es que se
referenciará a cualquier objeto que pertenezca a una de las clases no abstractas que
implementen ese interface.
4.9.- EJERCICIOS
• Para evitar que esta clase la tenga que manipular directamente el usuario
incorporaremos a la clase ListaEnlazada un método enumeracion() que devuelva una
instancia de EnumeradorLista inicializada sobre la propia lista.
• Así, desde un programa podremos para recorrer cualquier lista y hacer algo así como
java.util.Enumeration e = l.enumeracion();
while (e.hasMoreElements())
{
System.out.print( " " + e.nextElement() );
}
4-13
Laboratorio de Informática II - Interfaces
Crear por otro lado una clase Pizarra que tenga un atributo que sea un array de Figuras.
Además, tendrá un método para añadir Figuras al array y otro para borrar todas las figuras del
array.
Para probar el funcionamiento, realizar una clase Ejemplo que añada varias figuras (de
cualquier tipo) a la pizarra. Ir viendo lo que saldría por pantalla para ver cómo funciona el
polimorfismo. También se puede llamar al método borrar de la clase Pizarra para ver cómo se
irían borrando todas las figuras.
Definir un interface llamado Coloreable que hace referencia a todos los objetos que
admiten un color, definiendo los métodos void cambiaColor( Color c ) que cambia el
color del objeto y el Color queColor() que devuelve el color del objeto.
Persona Ave
Aparato
Piolin
TV Radio Lavadora
En la siguiente tabla se muestran las características mínimas que deben poseer cada una de
estas clases:
Atributos: Nombre y Edad
CLASE PERSONA
Métodos: Constructor(nombre,edad)
Atributos: Nombre, Edad, Carrera y Curso
CLASE ALUMNO
Métodos: Constructor(nombre,edad,carrera,curso)
4-14
Laboratorio de Informática II - Interfaces
• Implementar la jerarquía de clases de la figura 4.4 junto con los atributos y métodos
de cada una de estas clases.
• Hacer que todas las clases que representen a entidades con la capacidad de hablar
implementen este interface (éstas son las clases que en la figura 4.4 aparecen
sombreadas).
Cada una de estas clases debe implementar este interfaz de manera que el método
“hablar()” visualice por pantalla el mensaje “Hola, soy un <CLASE> y sé hablar”,
junto con los valores de los atributos del objeto (ver la salida por pantalla al final de
este ejercicio para orientarse).
4-15
Laboratorio de Informática II - Interfaces
Una vez hecho esto, el programa debería generar una salida por pantalla parecida a esta:
Hola, soy un LORO y sé hablar.
Sexo: Macho Edad: 2
Region: Europa Color: Azul
4-16