martes, 4 de junio de 2013

Clases Abstractas



Clases abstractas
 
La abstracción es un recurso de la mente (quizás el más característico de nuestra pretendida superioridad respecto del mundo animal). Por su parte, los lenguajes de programación permiten expresar la solución de un problema de forma comprensible simultáneamente por la máquina y el humano. Constituyen un puente entre la abstracción de la mente y una serie de instrucciones ejecutables por un dispositivo electrónico. En consecuencia, la capacidad de abstracción es una característica deseable de los lenguajes artificiales, pues cuanto mayor sea, mayor será su aproximación al lado humano. Es decir, con la imagen existente en la mente del programador. En este sentido, la introducción de las clases en los lenguajes orientados a objetos ha representado un importante avance respecto de la programación tradicional y dentro de ellas, las denominadas clases abstractas son las que representan el mayor grado de abstracción.
De hecho, las clases abstractas presentan un nivel de "abstracción" tan elevado que no sirven para instanciar objetos de ellas. Representan los escalones más elevados de algunas jerarquías de clases y solo sirven para derivar otras clases, en las que se van implementando detalles y concreciones, hasta que finalmente presentan un nivel de definición suficiente que permita instanciar objetos concretos. Se suelen utilizar en aquellos casos en que se quiere que una serie de clases mantengan una cierta característica o interfaz común. Por esta razón a veces se dice de ellas que son pura interfaz.
Resulta evidente en el ejemplo de la figura que los diversos tipos de motores tienen características diferentes. Realmente tienen poco en común un motor eléctrico de corriente alterna y una turbina de vapor. Sin embargo, la construcción de una jerarquía en la que todos motores desciendan de un ancestro común, la clase abstracta "Motores", presenta la ventaja de unificar la interfaz. Aunque evidentemente su definición será tan "abstracta", que no pueda ser utilizada para instanciar directamente ningún tipo de motor. El creador del lenguaje dice de ellas que soportan la noción de un concepto general del que solo pueden utilizarse variantes más concretas [2].







Una clase abstracta es la que tiene al menos una función virtual pura (como hemos visto, una función virtual es especificada como "pura" haciéndola igual a cero 4.11.8a).
§7  Función virtual pura
En ocasiones se lleva al extremo el concepto "virtual" en la declaración de una súper clase ("esta función será redefinida más tarde en las clases derivadas"), por lo que en ella solo existe una declaración de la función, relegándose las distintas definiciones a las clases derivadas. Entonces se dice que esta función es virtual pura. Esta circunstancia hay que advertirla al compilador; es una forma de decirle que la falta de definición no es un olvido por nuestra parte (de lo contrario el compilador nos señala que se nos ha olvidado la definición); esto se hace igualando a cero la declaración de la función [1]:
virtual int funct1(void);      // Declara función virtual
virtual int funct2(void) = 0;  // Declara función virtual pura

Como hemos señalado, la existencia de una función virtual basta para que la clase que estamos definiendo sea polimórfica ( 4.11.8). Si además igualamos la función a cero, la estaremos declarando como función virtual pura, lo que automáticamente declara la clase como abstracta ( 4.11.8c
Es muy frecuente que las funciones virtuales puras se declaren además con el calificador const ( 3.2.1c), de forma que es usual encontrar expresiones del tipo:
virtual int funct2(void) const = 0;  // Declara función virtual pura y constante


Nota: recordemos que las clases que tienen al menos una función virtual (o virtual pura) se denominan clases polimórficas ( 4.11.8). Resulta por tanto, que todas las clases abstractas son también polimórficas, pero no necesariamente a la inversa.
§3  Reglas de uso:
  • Una clase abstracta solo puede ser usada como clase base para otras clases, pero no puede ser instanciada para crear un objeto 1.
  • Una clase abstracta no puede ser utilizada como argumento o como retorno de una función 2.
  • Si puede declararse punteros-a-clase abstracta 3 [1].
  • Se permiten referencias-a-clase abstracta, suponiendo que el objeto temporal no es necesario en la inicialización 4.



 

Una clase que declara la existencia de métodos pero no la implementación de dichos métodos (o sea, las llaves { } y las sentencias entre ellas), se considera una clase abstracta.

Una clase abstracta puede contener métodos no-abstractos pero al menos uno de los métodos debe ser declarado abstracto.

Para declarar una clase o un metodo como abstractos, se utiliza la palabra reservada abstract.
abstract class Drawing
{
   abstract void miMetodo(int var1, int var2);
   String miOtroMetodo( ){ ... }
}

Una clase abstracta no se puede instanciar pero si se puede heredar y las clases hijas serán las encargadas de agregar la funcionalidad a los métodos abstractos. Si no lo hacen así, las clases hijas deben ser también abstractas. 




Un método abstracto es un método declarado en una clase para el cual esa clase no proporciona la implementación (el código). Una clase abstracta es una clase que tiene al menos un método abstracto. Una clase que extiende a una clase abstracta debe implementar los métodos abstractos (escribir el código) o bien volverlos a declarar como abstractos, con lo que ella misma se convierte también en clase abstracta. 

Declaración e implementación de métodos abstractos

Siguiendo con el ejemplo del apartado anterior, se puede escribir:
abstract class FiguraGeometrica {
    . . .
    abstract void dibujar();
    . . .
}

class Circulo extends FiguraGeometrica {
    . . .
    void dibujar() {
        // codigo para dibujar Circulo
        . . .
    }
La clase abstracta se declara simplemente con el modificador abstract en su declaración. Los métodos abstractos se declaran también con el mismo modificador, declarando el método pero sin implementarlo (sin el bloque de código encerrado entre {}). La clase derivada se declara e implementa de forma normal, como cualquier otra. Sin embargo si no declara e implementa los métodos abstractos de la clase base (en el ejemplo el método dibujar) el compilador genera un error indicando que no se han implementado todos los métodos abstractos y que, o bien, se implementan, o bien se declara la clase abstracta.

Referencias y objetos abstractos

Se pueden crear referencias a clases abstractas como cualquier otra. No hay ningún problema en poner:
FiguraGeometrica figura;
Sin embargo una clase abstracta no se puede instanciar, es decir, no se pueden crear objetos de una clase abstracta. El compilador producirá un error si se intenta:
FiguraGeometrica figura = new FiguraGeometrica();
Esto es coherente dado que una clase abstracta no tiene completa su implementación y encaja bien con la idea de que algo abstracto no puede materializarse.
Sin embargo utilizando el up-casting visto en el capítulo dedicado a la Herencia si se puede escribir:
FiguraGeometrica figura = new Circulo(. . .);
figura.dibujar();
La invocación al método dibujarse resolverá en tiempo de ejecución y la JVM llamará al método de la clase adecuada. En nuestro ejemplo se llamará al método dibujarde la clase Circulo.