Para la programación orientada a objetos, la abstracción es una de sus principales características. En Java, la abstracción de la POO se puede expresar de dos formas:interfaces y clases abstractas.Estas dos tienen demasiadas similitudes y demasiadas diferencias. Muchas personas al principio piensan que se pueden usar indistintamente, pero en realidad no es así. Hoy vamos a estudiar juntos las interfaces y las clases abstractas en Java.


I. Clase abstracta

Antes de comprender las clases abstractas, primero comprendamos los métodos abstractos. Un método abstracto es un método especial: solo tiene declaración, sin implementación concreta. El formato de declaración de un método abstracto es:

abstract void fun();

El método abstracto debe modificarse con la palabra clave `abstract`. Si una clase contiene un método abstracto, se denomina clase abstracta. La clase abstracta debe modificarse con la palabra clave `abstract` delante de la clase. Debido a que la clase abstracta contiene métodos sin implementación concreta, no se pueden crear objetos con una clase abstracta.

Ahora hay que prestar atención a un problema: en el libro *Pensar en Java*, la clase abstracta se define como "una clase que contiene métodos abstractos", pero más tarde se descubrió que si una clase no contiene métodos abstractos y solo se modifica con `abstract`, también es una clase abstracta. Es decir, una clase abstracta no necesariamente debe contener métodos abstractos. Personalmente creo que esto es un tema de buscarle tres pies al gato, porque si una clase abstracta no contiene ningún método abstracto, ¿por qué diseñarla como clase abstracta? Así que por ahora recordemos este concepto, no es necesario profundizar en el porqué.

[public] abstract class ClassName {
    abstract void fun();
}

De esto se puede ver que la clase abstracta existe para la herencia. Si defines una clase abstracta y no la heredas, entonces has creado esta clase abstracta en vano, porque no puedes usarla para hacer nada. Para una clase padre, si un método suyo no tiene sentido implementarlo en la clase padre y debe implementarse de manera diferente según las necesidades reales de las subclases, entonces se puede declarar este método como método `abstract`, y en ese momento la clase también se convierte en una clase `abstract`.

Las clases que contienen métodos abstractos se denominan clases abstractas, pero eso no significa que en una clase abstracta solo pueda haber métodos abstractos. Al igual que una clase normal, también puede tener variables miembro y métodos miembro normales. Nota: las principales diferencias entre una clase abstracta y una clase normal son tres:

  • 1) Los métodos abstractos deben ser `public` o `protected` (porque si son `private`, no pueden ser heredados por las subclases y estas no podrían implementarlos); por defecto, son `public`.
  • 2) Las clases abstractas no se pueden usar para crear objetos;
  • 3) Si una clase hereda de una clase abstracta, la subclase debe implementar los métodos abstractos de la clase padre. Si la subclase no implementa los métodos abstractos de la clase padre, entonces la subclase también debe definirse como clase `abstract`.
  • En otros aspectos, las clases abstractas no se diferencian de las clases normales.

II. Interfaz

Interfaz, en inglés *interface*, en ingeniería de software se refiere generalmente a métodos o funciones que se ponen a disposición de otros para que los llamen. De aquí podemos apreciar la intención original de los diseñadores del lenguaje Java: es una abstracción del comportamiento. En Java, la forma de definir una interfaz es la siguiente:

[public] interface InterfaceName {
 
}

La interfaz puede contener variables y métodos. Pero hay que tener en cuenta que las variables en la interfaz se especifican implícitamente como variables `public static final` (y solo pueden ser variables `public static final`; modificarlas con `private` provocará un error de compilación), mientras que los métodos se especifican implícitamente como métodos `public abstract` y solo pueden ser métodos `public abstract` (modificarlos con otras palabras clave, como `private`, `protected`, `static`, `final`, etc., provocará un error de compilación), y todos los métodos de la interfaz no pueden tener una implementación concreta, es decir, todos los métodos de la interfaz deben ser métodos abstractos. De aquí se puede vislumbrar la diferencia entre interfaz y clase abstracta: la interfaz es un tipo extremadamente abstracto, es más "abstracta" que la clase abstracta, y generalmente no se definen variables en la interfaz.

Para que una clase implemente un conjunto específico de interfaces se necesita la palabra clave `implements`, el formato concreto es el siguiente:

class ClassName implements Interface1,Interface2,[....]{
}

Se puede ver que se permite que una clase implemente múltiples interfaces específicas. Si una clase no abstracta implementa una interfaz, debe implementar todos los métodos de esa interfaz. Para una clase abstracta que implementa una interfaz, puede no implementar los métodos abstractos de la interfaz.


Diferencias entre clases abstractas e interfaces

1. Diferencias a nivel de sintaxis

  • 1) Una clase abstracta puede proporcionar los detalles de implementación de los métodos miembro, mientras que en una interfaz solo pueden existir métodos `public abstract`;
  • 2) Las variables miembro en una clase abstracta pueden ser de varios tipos, mientras que las variables miembro en una interfaz solo pueden ser de tipo `public static final`;
  • 3) Una interfaz no puede contener bloques de código estático ni métodos estáticos, mientras que una clase abstracta puede tener bloques de código estático y métodos estáticos;
  • 4) Una clase solo puede heredar de una clase abstracta, pero una clase puede implementar múltiples interfaces.

2. Diferencias a nivel de diseño

1) La clase abstracta es una abstracción de una cosa, es decir, una abstracción de la clase, mientras que la interfaz es una abstracción del comportamiento. La clase abstracta abstrae toda la clase en su conjunto, incluyendo atributos y comportamiento, pero la interfaz abstrae una parte local (el comportamiento) de la clase. Pongamos un ejemplo sencillo: los aviones y las aves son cosas de diferentes categorías, pero tienen algo en común: ambas pueden volar. Entonces, al diseñar, se puede diseñar el avión como una clase `Airplane` y el ave como una clase `Bird`, pero no se puede diseñar la característica devolartambién como una clase, ya que es solo una característica de comportamiento, no una descripción abstracta de una categoría de cosas. En este momento, se puede diseñar "volar" como una interfaz `Fly`, que contiene el método `fly()`, y luego `Airplane` y `Bird` implementan la interfaz `Fly` según sus propias necesidades. En cuanto a los diferentes tipos de aviones, como aviones de combate y aviones civiles, basta con que hereden directamente de `Airplane`; con las aves es similar, los diferentes tipos de aves heredan directamente de la clase `Bird`. De aquí se puede ver que la herencia es una relación de "es un", mientras que la implementación de una interfaz es una relación de "tiene un". Si una clase hereda de una clase abstracta, la subclase es necesariamente un tipo de la clase abstracta; mientras que la implementación de una interfaz es si se tiene o no, si se posee o no. Por ejemplo, si un ave puede volar (o si posee la característica de volar), entonces puede implementar esta interfaz; si no puede volar, no la implementa.

2) El nivel de diseño es diferente: la clase abstracta, como clase padre de muchas subclases, es un diseño de plantilla. La interfaz es una especificación de comportamiento, es un diseño radial. ¿Qué es un diseño de plantilla? El ejemplo más simple: todos han usado plantillas en PPT. Si se usa la plantilla A para diseñar el PPT B y el PPT C, la parte común de PPT B y PPT C es la plantilla A. Si la parte común necesita cambios, solo se necesita cambiar la plantilla A, no es necesario volver a modificar PPT B y PPT C. En cuanto al diseño radial, por ejemplo, si un ascensor está equipado con un cierto tipo de alarma, una vez que haya que actualizar la alarma, se deben actualizar todas. Es decir, para una clase abstracta, si se necesita agregar un nuevo método, se puede agregar directamente una implementación concreta en la clase abstracta y las subclases no necesitan cambiarse; pero para una interfaz no es posible, si la interfaz cambia, todas las clases que implementan esa interfaz deben modificarse en consecuencia.

Veamos el ejemplo más difundido en Internet: el ejemplo de la puerta y la alarma: las puertas tienenopen()yclose()dos acciones. En este momento podemos definir este concepto abstracto mediante una clase abstracta y una interfaz:

abstract class Door { public abstract void open(); public abstract void close(); }

O:

interface Door { public abstract void open(); public abstract void close(); }

Pero ahora, si necesitamos que la puerta tenga la función de alarma, ¿cómo deberíamos implementarlo? A continuación se ofrecen dos ideas:

1) Poner estas tres funciones en la clase abstracta, pero de esta manera todas las subclases que hereden de esta clase abstracta tendrán la función de alarma, aunque algunas puertas no necesariamente la tengan;

2) Poner estas tres funciones en la interfaz; la clase que necesite la función de alarma tendrá que implementar los métodos `open()` y `close()` de esta interfaz, quizás esa clase ni siquiera tenga las funciones `open()` y `close()`, como por ejemplo una alarma de incendios.

De aquí se puede ver que `open()`, `close()` y `alarm()` de `Door` pertenecen a comportamientos de dos categorías completamente diferentes: `open()` y `close()` son características de comportamiento inherentes a la puerta misma, mientras que `alarm()` es un comportamiento adicional y extendido. Por lo tanto, la mejor solución es diseñar la alarma por separado como una interfaz que contenga el comportamiento `alarm()`, y diseñar `Door` como una clase abstracta separada que contenga los comportamientos `open` y `close`. Luego diseñar una clase `AlarmDoor` que herede de la clase `Door` e implemente la interfaz `Alarm`.

interface Alram { void alarm(); } abstract class Door { void open(); void close(); } class AlarmDoor extends Door implements Alarm { void oepn() { //.... } void close() { //.... } void alarm() { //.... } }

Dirección original: http://www.cnblogs.com/dolphin0520/p/3811437.html