Cuándo usar: método predeterminado de la interfaz Java 8+, versus método abstracto

540

Java 8 permite la implementación predeterminada de métodos en interfaces llamadas Métodos predeterminados .

Estoy confundido entre cuándo usaría ese tipo de interface default method, en lugar de un abstract class(con abstract method(s)).

Entonces, ¿cuándo se debe usar la interfaz con los métodos predeterminados y cuándo se debe usar una clase abstracta (con método (s) abstracto)? ¿Siguen siendo útiles las clases abstractas en ese escenario?

Narendra Pathai
fuente
38
¿Quizás todavía no puede tener campos, métodos privados, etc. en las interfaces, mientras que puede tener clases abstractas?
sp00m
2
Me preguntaba sobre este tema antes, ahora estoy claro. Gracias a @Narendra Pathai. Me gustaría agregar un enlace de otro hilo que usted haya preguntado sobre el mismo tema, ya que ambas fueron mis dudas. stackoverflow.com/questions/19998309/…
Ashutosh Ranjan
Puede encontrar una buena publicación sobre esta aquí: blog.codefx.org/java/everything-about-default-methods
Fabri Pautasso
En ocasiones, aún puede codificar una clase base como una interfaz, incluso si la clase base tiene estado. Es solo que la interfaz tiene que definir establecedores y captadores para el estado y las clases concretas tienen que implementarlos y definir el campo. Una restricción sobre esto es que en una clase abstracta, la propiedad del bean puede ser privada o protegida. En las interfaces solo tienen métodos públicos. Entonces, una razón por la que usaría una clase base abstracta es si sus clases tienen una propiedad que necesita ser privada o protegida.
DaBlick
@DaBlick ¿No podría resolver el problema de estado en una interfaz a través de un HashMap? Ej: si quieres una clase Foo que contenga int a, b, String c. y desea que tengan estado, cree un HashMap </ * nombre del objeto Foo * / String, / * map of fields * / Hashmap </ * name específico Field * / String, / * field value * / Object >> map . Cuando quiere "instanciar" la clase teórica Foo, tiene el método, instanciar (String nameOfFoo) que hace map.put (nameOfFoo, fields) donde fields es un HashMap <String, Object> fields.put ("a", new int ("5")); fields.put ("b", new int ("6")); fields.put ("c", "blah"));
George Xavier

Respuestas:

307

Hay mucho más para las clases abstractas que las implementaciones de métodos predeterminadas (como el estado privado), pero a partir de Java 8, siempre que tenga la opción, debe ir con el defaultmétodo defender (aka ) en la interfaz.

La restricción del método predeterminado es que solo se puede implementar en términos de llamadas a otros métodos de interfaz, sin referencia al estado de una implementación en particular. Por lo tanto, el principal caso de uso son los métodos de mayor nivel y conveniencia.

Lo bueno de esta nueva característica es que, donde antes se veía obligado a usar una clase abstracta para los métodos de conveniencia, lo que limitaba al implementador a una herencia única, ahora puede tener un diseño realmente limpio con solo la interfaz y un mínimo de implementación esfuerzo forzado en el programador.

La motivación original para introducir defaultmétodos en Java 8 fue el deseo de extender las interfaces de Framework de colecciones con métodos orientados a lambda sin romper ninguna implementación existente. Aunque esto es más relevante para los autores de las bibliotecas públicas, también puede encontrar la misma característica útil en su proyecto. Tienes un lugar centralizado donde agregar nuevas comodidades y no tienes que depender de cómo se ve el resto de la jerarquía de tipos.

Marko Topolnik
fuente
34
Según este razonamiento, lo siguiente que agregarían es las declaraciones de método predeterminadas. Todavía no estoy seguro de esto, la función me parece más un truco que está siendo expuesto a todos por mal uso.
pantera
3
El único uso de las clases abstractas en la era de Java 8 que puedo ver es para definir campos no finales. En las interfaces, los campos son por defecto finales, por lo que no puede cambiarlos una vez que están asignados.
Anuroop
77
@Anuroop No solo por defecto, esa es la única opción. Las interfaces no pueden declarar el estado de la instancia, por lo que las clases abstractas están aquí para quedarse.
Marko Topolnik
2
@PhilipRego Los métodos abstractos no llaman a nada porque no tienen implementación. Los métodos implementados en una clase pueden acceder al estado de la clase (variables de instancia). Las interfaces no pueden declararlas, por lo que los métodos predeterminados no pueden acceder a ellas. Tienen que confiar en que la clase proporcione un método implementado que acceda al estado.
Marko Topolnik
2
Marko Topolnik, tu respuesta es acertada. Pero me gustaría recomendar una actualización a su respuesta. Es posible que desee agregar que la belleza de los métodos predeterminados es que, si la interfaz agrega nuevos métodos predeterminados, su implementación anterior de esa interfaz no se romperá. Esto no era cierto antes de Java 8.
hfontanez
125

Hay algunas diferencias técnicas. Las clases abstractas aún pueden hacer más en comparación con las interfaces Java 8:

  1. La clase abstracta puede tener un constructor.
  2. Las clases abstractas son más estructuradas y pueden contener un estado.

Conceptualmente, el objetivo principal de los métodos de defensa es una compatibilidad con versiones anteriores después de la introducción de nuevas características (como funciones lambda) en Java 8.

Vadym Vasyliev
fuente
20
Esta respuesta es realmente correcta y tiene sentido especialmente "Conceptualmente, el objetivo principal de los métodos de defensa es una compatibilidad con versiones anteriores"
Intentando el
1
@Desconocido esta página ofrece más información: docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
bernie
@UnKnown, básicamente le permite agregar métodos a una interfaz y las clases que implementan esa interfaz obtienen automáticamente esa funcionalidad.
LegendLength
3
Un punto más sutil sobre el punto no. 2 arriba sobre "puede retener el estado es esto". Las clases abstractas pueden mantener un estado que se puede cambiar más tarde. Las interfaces también pueden mantener el estado, pero una vez que se asigna un estado después de la creación de la instancia, el estado no se puede cambiar.
Anuroop
3
@Anuroop No describiría los public static finalcampos de una interfaz como "estado". La staticparte significa que no están relacionados con una instancia particular en absoluto. Se asignan en la instanciación de clase , que no es lo mismo que después de la creación de la instancia .
geronimo
65

Esto se describe en este artículo . Piensa en las forEachcolecciones.

List<?> list = 
list.forEach(…);

ForEach aún no ha sido declarado por java.util.Listni la java.util.Collectioninterfaz. Una solución obvia sería simplemente agregar el nuevo método a la interfaz existente y proporcionar la implementación donde sea necesario en el JDK. Sin embargo, una vez publicado, es imposible agregar métodos a una interfaz sin romper la implementación existente.

El beneficio que aportan los métodos predeterminados es que ahora es posible agregar un nuevo método predeterminado a la interfaz y no interrumpe las implementaciones.

Masudul
fuente
1
'es imposible agregar métodos a una interfaz sin romper la implementación existente', ¿verdad?
Andrey Chaschev
26
@AndreyChaschev Si agrega un nuevo método a la interfaz, todos los implementadores deben implementar ese nuevo método. Por lo tanto, rompe las implementaciones existentes.
Marko Topolnik el
44
@MarkoTopolnik gracias, me perdí eso. Solo por mencionar que hay una manera de evitar esto parcialmente , presentando este método en una implementación abstracta predeterminada. Para este ejemplo, esto sería AbstractList::forEacharrojar un UnsupportedOperationException.
Andrey Chaschev
3
@AndreyChaschev Sí, esa era la forma antigua (khm ... es la forma actual :), con la deficiencia de que restringe al implementador a la herencia única de la implementación abstracta proporcionada.
Marko Topolnik el
No me romperé si sucede que, de antemano, todas las implementaciones incluyeron ese método. Lo cual es poco probable pero posible.
George Xavier
19

Estos dos son bastante diferentes:

Los métodos predeterminados son agregar funcionalidad externa a las clases existentes sin cambiar su estado.

Y las clases abstractas son un tipo normal de herencia, son clases normales que están destinadas a extenderse.

Andrey Chaschev
fuente
18

Como se describe en este artículo,

Clases abstractas versus interfaces en Java 8

Después de introducir el Método predeterminado, parece que las interfaces y las clases abstractas son las mismas. Sin embargo, siguen siendo un concepto diferente en Java 8.

La clase abstracta puede definir constructor. Están más estructurados y pueden tener un estado asociado con ellos. Por el contrario, el método predeterminado solo se puede implementar en términos de invocar otros métodos de interfaz, sin referencia al estado de una implementación particular. Por lo tanto, ambos se usan para diferentes propósitos y elegir entre dos realmente depende del contexto del escenario.

Sufiyan Ghori
fuente
Creo que la clase abstracta tiene un constructor que se puede definir a diferencia de la interfaz. En Java 8 también ambos son diferentes entre sí debido a esto.
Hemanth Peela
1
¿Por qué una clase abstracta tiene un constructor si no se puede instanciar?
George Xavier
Podemos llamar a super () desde la clase secundaria que llamará al constructor de la clase abstracta. Esto afecta el estado de la clase abstracta.
Sujay Mohan
14

En cuanto a su consulta de

Entonces, ¿cuándo se debe usar la interfaz con los métodos predeterminados y cuándo se debe usar una clase abstracta? ¿Siguen siendo útiles las clases abstractas en ese escenario?

La documentación de Java proporciona una respuesta perfecta.

Clases abstractas comparadas con las interfaces:

Las clases abstractas son similares a las interfaces. No puede crear instancias de ellos, y pueden contener una combinación de métodos declarados con o sin implementación.

Sin embargo, con las clases abstractas, puede declarar campos que no son estáticos y finales, y definir métodos concretos públicos, protegidos y privados.

Con las interfaces, todos los campos son automáticamente públicos, estáticos y finales, y todos los métodos que declara o define (como métodos predeterminados) son públicos. Además, puede extender solo una clase, ya sea abstracta o no, mientras que puede implementar cualquier cantidad de interfaces.

Los casos de uso para cada uno de ellos se explicaron en la siguiente publicación SE:

¿Cuál es la diferencia entre una interfaz y una clase abstracta?

¿Siguen siendo útiles las clases abstractas en ese escenario?

Si. Todavía son útiles. Pueden contener métodos y atributos no estáticos, no finales ( protegidos, privados además de públicos ), lo que no es posible incluso con interfaces Java-8.

Ravindra babu
fuente
Ahora las interfaces también tienen métodos privados howtodoinjava.com/java9/java9-private-interface-methods
valijon
13

Siempre que tengamos una opción entre la clase abstracta y la interfaz, siempre (casi) preferimos los métodos predeterminados (también conocidos como defensor o extensiones virtuales).

  1. Los métodos predeterminados han puesto fin al patrón clásico de interfaz y una clase complementaria que implementa la mayoría o todos los métodos en esa interfaz. Un ejemplo es Collection and AbstractCollection. Ahora deberíamos implementar los métodos en la interfaz misma para proporcionar la funcionalidad predeterminada. Las clases que implementan la interfaz tienen la opción de anular los métodos o heredar la implementación predeterminada.
  2. Otro uso importante de los métodos predeterminados es interface evolution. Supongamos que tuviera una clase Ball como:

    public class Ball implements Collection { ... }

Ahora en Java 8 se introduce una nueva característica de flujos. Podemos obtener una transmisión usando el streammétodo agregado a la interfaz. Si streamno fuera un método predeterminado, todas las implementaciones para la Collectioninterfaz se habrían roto ya que no estarían implementando este nuevo método. Agregar un método no predeterminado a una interfaz no lo es source-compatible.

Pero supongamos que no recompilamos la clase y usamos un archivo jar antiguo que contiene esta clase Ball. La clase se cargará bien sin este método faltante, se pueden crear instancias y parece que todo está funcionando bien. PERO si el programa invoca el streammétodo en caso de Ballque lo obtengamos AbstractMethodError. Por lo tanto, hacer que el método predeterminado resolviera ambos problemas.

akhil_mittal
fuente
9

Los métodos predeterminados en la interfaz Java permiten la evolución de la interfaz .

Dada una interfaz existente, si desea agregarle un método sin romper la compatibilidad binaria con versiones anteriores de la interfaz, tiene dos opciones: agregar un método predeterminado o estático. De hecho, cualquier método abstracto agregado a la interfaz tendría que ser implementado por las clases o interfaces que implementan esta interfaz.

Un método estático es exclusivo de una clase. Un método predeterminado es exclusivo de una instancia de la clase.

Si agrega un método predeterminado a una interfaz existente, las clases e interfaces que implementan esta interfaz no necesitan implementarla. Ellos pueden

  • implementa el método predeterminado y anula la implementación en la interfaz implementada.
  • volver a declarar el método (sin implementación) que lo hace abstracto.
  • no hacer nada (entonces el método predeterminado de la interfaz implementada simplemente se hereda).

Más sobre el tema aquí .

Kiriloff
fuente
7

Aunque es una vieja pregunta, permítanme dar mi opinión al respecto también.

  1. clase abstracta: dentro de la clase abstracta podemos declarar variables de instancia, que se requieren para la clase secundaria

    Interfaz: la interfaz interna de cada variable siempre es pública estática y final, no podemos declarar variables de instancia

  2. clase abstracta: la clase abstracta puede hablar sobre el estado del objeto

    Interfaz: la interfaz nunca puede hablar sobre el estado del objeto

  3. clase abstracta: dentro de la clase abstracta podemos declarar constructores

    Interfaz: Dentro de la interfaz no podemos declarar constructores ya que el propósito de los
    constructores es inicializar las variables de instancia. Entonces, ¿cuál es la necesidad del constructor allí si no podemos tener variables de instancia en las interfaces ?

  4. clase abstracta: dentro de la clase abstracta podemos declarar instancias y bloques estáticos

    Interfaz: las interfaces no pueden tener bloques de instancia y estáticos.

  5. clase abstracta: la clase abstracta no puede referirse a la expresión lambda

    Interfaces: las interfaces con un método abstracto único pueden referirse a la expresión lambda

  6. clase abstracta : dentro de la clase abstracta podemos anular los métodos de CLASE DE OBJETO

    Interfaces: no podemos anular los métodos OBJECT CLASS dentro de las interfaces.

Terminaré con la nota de que:

Los conceptos de métodos predeterminados / conceptos de métodos estáticos en la interfaz llegaron solo para guardar clases de implementación pero no para proporcionar una implementación útil significativa. Métodos predeterminados / métodos estáticos son una especie de aplicación ficticia "si lo desea, puede usarlos o se los puede sustituir (en el caso de los métodos por defecto) en la clase de implementación" Por lo tanto salvándonos de la aplicación de nuevos métodos en clases de implementación cada vez que nuevos métodos en las interfaces Se agregan. Por lo tanto, las interfaces nunca pueden ser iguales a las clases abstractas.

Umar Tahir
fuente
5

La regla de Remi Forax es No diseñas con clases abstractas. Diseñas tu aplicación con interfaces . Watever es la versión de Java, sea cual sea el lenguaje. Está respaldada por la I principio de la segregación Nterface en SOL I D principios.

Más tarde puede usar las clases abstractas para factorizar el código. Ahora con Java 8 puedes hacerlo directamente en la interfaz. Esta es una instalación, no más.

Nicolas Zozol
fuente
2

¿Cuándo se debe utilizar la interfaz con los métodos predeterminados y cuándo se debe utilizar una clase abstracta?

Compatibilidad con versiones anteriores: imagine que su interfaz es implementada por cientos de clases, modificando esa interfaz obligará a todos los usuarios a implementar el método recién agregado, aunque no sea esencial para muchas otras clases que implementan su interfaz, además permite su interfaz ser una interfaz funcional

Hechos y restricciones:

1-Solo se puede declarar dentro de una interfaz y no dentro de una clase o clase abstracta.

2-Debe proporcionar un cuerpo

3-No se supone que sea abstracto como otros métodos normales utilizados en una interfaz.

Ahmad Sanie
fuente
1

En Java 8, una interfaz se parece a una clase abstracta, aunque puede haber algunas diferencias como:

1) Las clases abstractas son clases, por lo que no están restringidas a otras restricciones de la interfaz en Java, por ejemplo, la clase abstracta puede tener el estado , pero usted no puede tener el estado en la interfaz en Java.

2) Otra diferencia semántica entre la interfaz con métodos predeterminados y la clase abstracta es que puede definir constructores dentro de una clase abstracta , pero no puede definir el constructor dentro de la interfaz en Java

Manish Sahni
fuente
Estoy de acuerdo con el n. ° 2 pero para el n. ° 1, ¿no puede simplemente implementar la interfaz y así tener un estado a través de la clase de implementación?
George Xavier
0

Los métodos predeterminados en la Interfaz Java se utilizarán más para proporcionar una implementación ficticia de una función, lo que salvará a cualquier clase de implementación de esa interfaz de la molestia de declarar todos los métodos abstractos, incluso si solo quieren tratar con uno. Por lo tanto, los métodos predeterminados en la interfaz son, en cierto modo, un reemplazo del concepto de clases de adaptador.

Sin embargo, se supone que los métodos en la clase abstracta brindan una implementación significativa que cualquier clase secundaria debe anular solo si es necesario para anular una funcionalidad común.

shubh
fuente
0

Como se mencionó en otras respuestas, se agregó la capacidad de agregar implementación a una interfaz para proporcionar compatibilidad con versiones anteriores en el marco de Colecciones. Yo diría que proporcionar compatibilidad con versiones anteriores es potencialmente la única buena razón para agregar implementación a una interfaz.

De lo contrario, si agrega implementación a una interfaz, está violando la ley fundamental de por qué las interfaces se agregaron en primer lugar. Java es un lenguaje de herencia único, a diferencia de C ++ que permite la herencia múltiple. Las interfaces proporcionan los beneficios de escritura que vienen con un lenguaje que admite la herencia múltiple sin presentar los problemas que vienen con la herencia múltiple.

Más específicamente, Java solo permite la herencia única de una implementación, pero sí permite la herencia múltiple de interfaces. Por ejemplo, el siguiente es un código Java válido:

class MyObject extends String implements Runnable, Comparable { ... }

MyObject hereda solo una implementación, pero hereda tres contratos.

Java transmitió la herencia múltiple de la implementación porque la herencia múltiple de la implementación viene con una serie de problemas espinosos, que están fuera del alcance de esta respuesta. Se agregaron interfaces para permitir la herencia múltiple de contratos (también conocidas como interfaces) sin los problemas de herencia múltiple de la implementación.

Para apoyar mi punto, aquí hay una cita de Ken Arnold y James Gosling del libro The Java Programming Language, cuarta edición :

La herencia única excluye algunos diseños útiles y correctos. Los problemas de la herencia múltiple surgen de la herencia múltiple de la implementación, pero en muchos casos se usa la herencia múltiple para heredar varios contratos abstractos y quizás una implementación concreta. Proporcionar un medio para heredar un contrato abstracto sin heredar una implementación permite los beneficios de tipificación de la herencia múltiple sin los problemas de la herencia de implementación múltiple. La herencia de un contrato abstracto se denomina herencia de interfaz . El lenguaje de programación Java admite la herencia de interfaces al permitirle declarar un interfacetipo

johnnyodonnell
fuente
-1

Piense primero en el principio abierto / cerrado. Los métodos predeterminados en las interfaces lo VIOLAN. Esta es una mala característica en Java. Fomenta el mal diseño, la mala arquitectura, la baja calidad del software. Sugeriría evitar usar completamente los métodos predeterminados.

Hágase algunas preguntas: ¿Por qué no puede poner sus métodos en la clase abstracta? ¿Necesitarías entonces más de una clase abstracta? Luego piense de qué es responsable su clase. ¿Estás seguro de que todos los métodos que vas a poner en la clase única realmente cumplen el mismo propósito? Puede ser que distinguirá varios propósitos y luego dividirá su clase en varias clases, para cada propósito su propia clase.

Mentallurg
fuente