Sunteți pe pagina 1din 12

Clases abstractas e interfaces

1. CLASES ABSTRACTAS

E n esta lección estudiaremos dos conceptos importantes del lenguaje Java, especialmente con el propósito de

entender cómo utilizarlos posteriormente cuando hagamos uso de los paquetes de Java que tenemos a nuestra disposición a la hora de crear aplicaciones.

Estos

conceptos

son

el

de

clases

Empecemos por las primeras.

abstractas

e

interfaces.

En ocasiones, se escriben clases que no van a instanciarse, es decir, cuyo objetivo no es que se creen objetos de dicha clase, sino que su propósito es que sean extendidas por otras subclases, de las cuales sí se crearán objetos.

Con un ejemplo lo verá más claro. Imagine que está creando una aplicación de dibujo. Ha analizado el problema y ha llegado a la conclusión de que podrá dibujar figuras cuadradas, figuras redondas, etc., por lo que crea una clase llamada Figura.

Sin embargo, se da cuenta de que los distintos tipos de figuras tienen características únicas. Por ejemplo: un círculo se dibuja conociendo su radio mientras que para dibujar un cuadrado necesitamos un punto inicial y un punto final.

Esto le hace llegar a la conclusión de que no va a dibujar figuras, sino tipos de figuras. Es decir, no va a crear objetos de la clase Figura, sino de alguna de sus subclases.

¿Por qué? Pues porque realmente no existen figuras como tal, sino que encontraremos círculos, cuadrados, etc. Es decir, que los objetos que encontremos pertenecerán a las subclases de la clase Figura y no a ésta. En estos casos, la clase superior es una clase abstracta.

Su propósito es incorporar las características comunes a todas las subclases, pero no se crearán objetos de dicha clase.

public abstract class Figura { public abstract double longitud(); public abstract double superficie(); public abstract double volumen();

}

Con la palabra abstract está indicando justamente esta circunstancia. A partir de ahora, si intenta crear objetos de la clase Figura, producirá un error.

Copyright © Computer Aided Education, S.A.

1

Clases abstractas e interfaces

La clase Figura proporcionará las características comunes a todas las figuras. Por ejemplo, podremos calcular la longitud de la figura, su superficie, su volumen, etc.

Los métodos también pueden tener el modificador abstract, indicando que deben ser sobrescritos en las subclases.

Un método abstracto se caracteriza por no tener código asociado. Usted indica que la clase tiene esa funcionalidad, pero no indica cómo la lleva a cabo ya que realmente depende del tipo de objeto que esté manejando.

Una clase abstracta también podría tener métodos no abstractos, en los que sí se escribe el código correspondiente.

El primer paso ya lo hemos realizado. Hemos definido una clase abstracta con métodos abstractos. Sin embargo, las clases abstractas no sirven de nada si no se extienden:

public class Cuadrado extends Figura { private double lado;

public Cuadrado(double lado) { this.lado = lado;

}

public double longitud() { return (4 * lado);

}

public double superficie() { return (lado * lado);

}

public double volumen() { return (lado * lado * lado);

}

}

Como la clase Cuadrado amplía o deriva la clase abstracta Figura, tiene que sobrescribir todos sus métodos abstractos. Si no lo hace, deberá declarar la clase Cuadrado también como abstracta.

Por lo tanto, estamos indicando cómo se calcula la longitud (perímetro), superficie y volumen de una figura cuadrada en concreto.

Además, puede ver cómo en este caso sí que tiene sentido incluir un constructor de clase, ya que sí que vamos a crear objetos de la clase Cuadrado.

Clases abstractas e interfaces

Repasemos lo que hemos estado haciendo:

Con la clase abstracta Figura hemos indicado las características comunes a todas las figuras que podemos dibujar. Es una clase abstracta ya que no tiene sentido crear objetos de la misma.

Con la subclase Cuadrado estamos permitiendo crear objetos de este tipo de figuras. Ya que es una subclase de una clase abstracta, está obligada a sobrescribir todos sus métodos abstractos o a declararse de nuevo como abstracta. Además, puede incorporar nuevas características únicas para este tipo de figuras.

De la misma forma podríamos escribir clases que representaran figuras redondas.

2. CLASES ABSTRACTAS DE JAVA

N o hace falta escribir nuevas clases para entender el concepto de clase o método abstracto.

El lenguaje Java incluye varias clases de este tipo, las cuales proporcionan métodos genéricos, que son heredados y sobrescritos por sus subclases.

Uno de los ejemplos más claros es la clase abstracta Number. Esta clase permite tratar valores numéricos como objetos.

Number . Esta clase permite tratar valores numéricos como objetos. Copyright © Computer Aided Education, S.A.

Copyright © Computer Aided Education, S.A.

3

Observe cómo los tipos de datos escala- res aparecen en minúsculas, mientras que las clases

Observe cómo los tipos de datos escala- res aparecen en minúsculas, mientras que las clases lo hacen con la primera letra en mayúscu- las.

Clases abstractas e interfaces

Fíjese en la aparición de la palabra abstract en la definición de la clase Number.

Los tipos de datos escalares, como int, long, double, etc. permiten trabajar con valores numéricos sin que sean tratados como objetos. Sin embargo, hay ocasiones en que es deseable que sí sean objetos. Para ello aparecen clases como Byte, Short, Integer, Long, Float o Double.

Todas estas clases derivan de la clase abstracta Number, que proporciona la funcionalidad básica.

Por ejemplo, la clase abstracta Number proporciona varios métodos que permiten convertir un determinado valor a un determinado tipo de datos:

byteValue: para devolver el valor del objeto Number como byte.

doubleValue: para devolver el valor del objeto Number como double. Es un método abstracto.

floatValue: para devolver el valor del objeto Number como float. Es un método abstracto.

intValue: para devolver el valor del objeto Number como int. Es un método abstracto.

longValue: para devolver el valor del objeto Number como long. Es un método abstracto.

shortValue: para devolver el valor del objeto Number como short.

Para que lo entienda mejor: imagine que ha creado un objeto de la clase Number y desea convertirlo a un valor double, entonces utilizaría el método doubleValue.

Sin embargo, no es lo mismo convertir un valor int a double que hacer lo propio con un valor float. Por eso, los métodos deben sobrescribirse en cada subclase.

Las subclases de Number son llamadas “envoltorios para los números”, ya que permiten trabajar con valores numéricos como si se tratara de objetos.

Veamos un caso práctico para que toda esta teoría se asimile mejor: imagine que está escribiendo una aplicación en la que necesita trabajar con valores double, pero sólo desea utilizar dos decimales (situación bastante cotidiana).

Clases abstractas e interfaces

NetBeans utiliza el icono para indicar que se trata de un método sobrescrito.

NetBeans utiliza el icono para indicar que se trata de un método sobrescrito.

NetBeans utiliza el icono para indicar que se trata de un método sobrescrito.

Utilizando el tipo de datos primitivo double esto no lo podría hacer, ya que debería controlar en todo momento los distintos resultados e ir truncando o redondeando todas las operaciones. Sin embargo, si creamos una nueva clase utilizando la clase Number como superclase, podremos trabajar con objetos que representen los valores deseados.

public class Double2 extends Number{

private Double value;

public Double2(double x) { this.value = new Double(x);

}

public int intValue()

{

return this.value.intValue();

}

public float floatValue()

{

return this.value.floatValue();

}

public long longValue()

{

return this.value.longValue();

}

public double doubleValue()

{

double aux = this.value.doubleValue(); int aux2; aux = ((aux * 100) + 0.5); aux2 = (int)aux; aux = (aux2 / 100.0); return aux;

}

}

El código anterior muestra una clase Double2 que extiende la clase abstracta Number, por lo que debe sobrescribir todos sus métodos abstractos.

El propósito de esta clase es poder trabajar con valores de tipo double, pero sólo con dos decimales.

Lo primero en lo que tenemos que fijarnos es que la nueva clase posee una propiedad llamada value. Esta propiedad es un objeto Double.

Copyright © Computer Aided Education, S.A.

5

Clases abstractas e interfaces Observe cómo el constructor crea el objeto que se corresponde con

Clases abstractas e interfaces

Observe cómo el constructor crea el objeto que se corresponde con la propiedad anteriormente dicha. Se crea un objeto Double a partir de un valor double.

Como se tienen que sobrescribir los métodos abstractos de Number, hacemos lo propio con intValue, floatValue y longValue.

Para ello, lo que se hace es llamar al método del mismo nombre de la clase Double.

La principal modificación se lleva a cabo en el método doubleValue. Recuerde que usted desea poder trabajar con valores double, pero teniendo en cuenta sólo dos decimales.

De esto se encarga el método doubleValue de los objetos que cree a partir de la nueva clase.

Sea x el valor double con un número indefinido de decimales, y sea y el valor "equivalente" con sólo dos decimales:

y = (int) (x * 100) + 0.5

-------------------------

100.0

Realizando esta operación obtendremos el valor double (observe cómo el resultado final es double al dividir por 100.0) con sólo dos decimales.

Lo que quiero que entienda es que lo único que necesitábamos era modificar la funcionalidad del método doubleValue, ya que este método es el que se utilizará cuando trabaje con operaciones en las que intervengan objetos de la clase Double2.

Para el resto de métodos, nos sirven los de la clase original Double.

Utilicemos ahora esta clase:

public class Main { public static void main(String[] args) { Double d1 = new Double(2.58989745); Double2 d2 = new Double2(2.58989745);

System.out.println(d1.doubleValue());

System.out.println(d2.doubleValue());

}

}

Clases abstractas e interfaces

Hemos declarado y creado dos objetos: uno de la clase Double y otro de la clase Double2. Ambos objetos se crean a partir del mismo valor double.

Posteriormente se imprime el valor double resultado de llamar al método doubleValue. Recuerde que la diferencia entre una y otra clase radica aquí.

Veamos el resultado:

2.58989745

para el primer resultado y

2.59

para el segundo resultado.

Como puede ver, el resultado obtenido al llamar al método doubleValue varía de una clase a otra.

El primer valor es el resultado que ofrece la clase estándar Double, mientras que el segundo es el resultado que devuelve la clase que hemos escrito.

Si usted tuviera que trabajar con valores de dos decimales en su aplicación (algo muy común), podría crear objetos de su clase

Double2.

3. CREAR INTERFACES U n concepto similar al de las clases abstractas pero pensado con otros propósitos es el de interfaz.

Las interfaces se utilizan muchísimo para normalizar o estandarizar el código de un proyecto grande, donde pueden intervenir varios programadores.

Las interfaces se crean con el propósito de establecer “un acuerdo” o “un contrato” entre el programador que utiliza la interfaz y el diseñador de esta última.

Una interfaz no es una clase pero sí un tipo de datos similar, ya que puede contener propiedades (constantes) y métodos, aunque sin el código de implementación de estos últimos, es decir, con su cuerpo vacío.

Copyright © Computer Aided Education, S.A.

7

Clases abstractas e interfaces

Cuando necesitemos utilizar una interfaz incluida en los paquetes de Java o de otro fabricante, nos veremos en la obligación de cumplir ese contrato o, dicho de forma correcta, de implementar la interfaz. Ahora lo veremos con un ejemplo.

public class Empleado{

//Propiedades private String nombre; protected int salario; private String fechaAlta; private String fechaBaja;

//Constructor public Empleado(String nombre, int salario, String fecha) { this.nombre = nombre; this.salario = salario; this.fechaAlta = fecha; this.fechaBaja = null;

}

public void darDeBaja(String fecha)

{

fechaBaja = fecha; salario = 0;

}

public int calcularSalario()

{

return salario;

}

}

En este proyecto ya hemos estado trabajando en alguna ocasión. Incluye la clase Empleado, con la que hemos representado los empleados de una empresa.

Estos empleados tienen un nombre, un salario, etc. Además, pueden darse de baja de la empresa, deberemos calcular su salario, etc.

Ahora imagine que usted pertenece a un equipo de programadores que está completando una aplicación de recursos humanos, por lo que ha diseñado esta clase.

El jefe del proyecto ha establecido algunas normas que deben cumplir todos los programadores. Y una de ellas es que todos tienen que utilizar un modo estándar a la hora de comparar los objetos de sus clases.

Clases abstractas e interfaces

Vamos a utilizar la nomenclatura de incluir una I al principio del nombre de las

Vamos a utilizar la nomenclatura de incluir una I al principio del nombre de las interfaces.

Para ello, ha creado una interfaz o contrato que deben cumplir los programadores. Tomemos ahora el papel del jefe de proyecto y declaremos la interfaz.

Haga clic con el botón derecho del ratón en el paquete donde vaya

a crear la interfaz (ventana Projects) y elija New - Java Interface.

Fíjese que NetBeans dispone de una opción específica para este tipo de datos, ya que no podemos utilizar la plantilla de una clase.

//IComparable.java

public interface IComparable { public int esMayorQue(IComparable otro);

}

En la declaración de la interfaz simplemente tenemos que listar las propiedades (constantes) y métodos que incluirá, pero sin código para estos últimos.

En este aspecto es muy parecido a las clases abstractas. En nuestra interfaz IComparable sólo incluiremos un método, que es el que permitirá asegurar si un objeto es más grande que otro.

En este caso estamos declarando el método esMayorQue sin incluir ningún detalle de su implementación. De ahí que sea importante incluir el punto y coma tras su declaración.

¿Qué es lo que nos está indicando la interfaz IComparable? Pues indica que todas las clases que requieran de un método para comparar sus objetos deben implementar la interfaz y esto significa escribir el código de todos y cada uno de sus métodos, respetando su declaración.

En este caso, esto significa que tiene que devolver un valor entero

y aceptar un parámetro del tipo de datos IComparable, es decir, de la propia interfaz.

Seguramente si usted estuviera escribiendo una clase de cadenas de texto, utilizaría el número de caracteres para compararlas; si su clase representara libros, podría utilizar el número de páginas, etc.

Pero en cualquier caso, tendría que implementar la interfaz IComparable para representar la forma en que se comparan dichos objetos y para ello, debería utilizar el método esMayorQue.

Copyright © Computer Aided Education, S.A.

9

Clases abstractas e interfaces

El objetivo de crear interfaces es establecer un contrato entre las personas que utilizan la interfaz (los programadores en este caso) y la persona que lo diseñó (el jefe de proyecto en este caso).

Usted ha escrito el contrato o interfaz como jefe del proyecto. Ahora debe implementarlo en su clase Empleado jugando el papel de un programador perteneciente al equipo de programación.

4. IMPLEMENTAR INTERFACES

A hora debemos ponernos en el papel del programador de la clase Empleado. Se ve en la necesidad de establecer

un método para comparar los empleados y sabe que debe hacerlo implementando la interfaz IComparable.

Ya sabemos que una clase puede extender otra más sencilla llamada superclase o clase base. Para ello, se utiliza la palabra clave extends. Bien, para indicar que una clase implementa una interfaz, se utiliza la palabra implements.

public class Empleado implements IComparable {

}

A diferencia de lo que ocurre con la herencia, una clase puede implementar más de una interfaz, para lo que las separaremos entre comas detrás de la palabra implements.

Éste es otro de los propósitos con el que se crean las interfaces, ya que podemos, por una parte, extender la funcionalidad de otras clases mediante la herencia, y añadir otras funcionalidades cumpliendo con los contratos que representan las interfaces.

Una vez indicado que la clase implementa la interfaz, incluimos el siguiente método en la clase Empleado:

public int esMayorQue(IComparable otro) { Empleado otroEmpleado = (Empleado)otro; if(this.calcularSalario() > otroEmpleado.calcularSalario()) return 1; else if (this.calcularSalario() == otroEmpleado.calcularSalario()) return 0;

else

}

return -1;

Clases abstractas e interfaces

Puede utilizar el comando Override Methods del menú Source para implementar los métodos de una interfaz. Para ello, active la opción Show Superclasses and Interfaces.

ello, active la opción Show Superclasses and Interfaces . ¿Y cómo comparamos dos empleados? ¿Qué criterio

¿Y cómo comparamos dos empleados? ¿Qué criterio utilizamos?

Un buen criterio podría ser el sueldo que cobran. Así pues, devolveremos un valor entero en función de la comparación entre el objeto actual y el objeto pasado como parámetro:

1: el objeto es mayor que el parámetro ("cobra más"). 0: los objetos son iguales ("cobran igual"). -1: el objeto es menor que el parámetro ("cobra menos")

Copyright © Computer Aided Education, S.A.

11

Si se incluyen variables en una interfaz, éstas tienen que ser realmente constantes, es decir,

Si se incluyen variables en una interfaz, éstas tienen que ser realmente constantes, es decir, public static final.

Clases abstractas e interfaces

Fíjese: como el método recibe un parámetro de tipo IComparable, debemos hacer un casting con la clase Empleado, ya que en este caso comparamos concretamente objetos Empleado.

Si estuviéramos escribiendo una clase Libro, deberíamos hacer

el casting con Libro, ya que compararíamos concretamente objetos

Libro.

Fíjese que se puede utilizar el método calcularSalario del objeto otroEmpleado, resultado de hacer el casting con el objeto pasado como parámetro.

Con esto hemos implementado la interfaz IComparable. El ejemplo ha sido sencillo porque dicha interfaz sólo incluía un método, pero podría ser todo lo complicado que necesitáramos, incluyendo tanto propiedades (constantes) como muchos más métodos.

Es conveniente ver una interfaz como un contrato. El creador de la interfaz se compromete a no cambiar su declaración, mientras que el programador de la clase que la implementa se ve obligado a implementar la interfaz tal como fue concebida.

Cuando utilicemos las clases de los paquetes estándar de Java

y

necesitemos implementar alguna interfaz, estaremos aceptando

el

diseño de la misma que realizó Sun o cualquier otro fabricante y

comprometiéndonos a implementarla siguiendo dicho diseño.