[Java] ¿Cuándo debería usar una abstract class vs interface?

widim

¿Cuándo debería usar una abstract class vs interface? No me queda claro las explicaciones que he buscado, sinceramente no veo las ventajas de extender una clase abstracta cuando las interfaces te ofrecen lo mismo y puedes implementar las que quieras en tu clase.

desu

#1 Dos respuestas:

Si aun estas en la escuela, haz lo que diga tu profesor.

En el mundo real, las abstract class nunca se utilizan ni deben ser utilizadas.

3
Leos

Si quieres tener más problemas de los que ya tengas con tu código usa clases abstractas

3
GodzillaPosh

En Java por ejemplo puedes implementar tantas interfaces como quieras mientras que solo puedes extender de una clase

Antes también existía la diferencia de que una abstract class te podía dar la implementación de un método, pero desde Java 8 las interfaces también pueden tener default implementations.

Otra diferencia es que la abstract class puede tener estado y la interface no, por eso es desaconsejable usar abstract classes.

1
Ranthas

En Java, las clases abstractas tenían su utilidad (y bastante) en versiones anteriores a la 1.8, debido a las limitaciones de las interfaces.

La principal diferencia entre una clase abstracta y una interfaz en Java, hoy por hoy, es que en la clase abstracta puedes definir atributos ya que es una clase, y en la interfaz no.

Con la inclusión de la keyword default, las interfaces casi han sustituido a las clases abstractas. Yo hace años que no he tenido necesidad de definir una.

3 2 respuestas
Kaledros

Dando una respuesta rápida y guarra, una interfaz se usa cuando dejas a las clases que la implementan elegir cómo implementar las funcionalidades. Una clase abstracta se usa cuando la clase implementa una funcionalidad pero quieres dejar que las subclases implementen alguna otra.

Por ejemplo.

Imagina que tienes una clase Vehicle y quieres implementar una funcionalidad para que todos los vehículos se muevan. Pues haces una interfaz Movable y le declaras el método move(). Cada Vehicle implementa Movable y declara move() como le sale del nabo. Otras interfaces pueden ser Flyable, por ejemplo.

Sin embargo, imagina que tienes una clase Car y quieres tener subclases por cada tipo de coche. La diferencia es que todos los coches comparten cierto comportamiento (acelerar pisando el mismo pedal, por ejemplo), pero arrancan de manera distinta (llave, botón, etc) de manera que puedes crear una clase abstracta Car que implemente el método speedUp() pero deje como abstracto el método turnOn() para que cada subclase de Car haga lo que quiera. Las subclases podrán reimplementar speedUp() si quieren, pero no es necesario, y sin embargo estarán obligadas a implementar turnOn().

4
Leos

Si quieres ahondar más en este tema, busca herencia vs composición y encontraras bastante info sobre este tema

3 2 respuestas
radykal

Haz caso a #7 composicion > herencia

1
overflow

Son lo mismo, con un enfoque diferente... Las interfaces son clases abstractas. En Java igual estará todo empaquetadito bonito.

En definitiva, son estructuras para implementar arquitecturas.

--- Edit

1 respuesta
Lecherito

#9 no son lo mismo y ya han explicado la diferencia en este post.

1 respuesta
wasiflo

La clase abstracta te sirve para meter todas las dependencias que tenga tu proyecto. Así limpias la clase principal y se mantiene mejor el código.

1 respuesta
Leos

> Clase abstracta
> mantener mejor el código
> pick one

Lecherito

#11 ???

A eso no le hagas caso anda xD

1 respuesta
wasiflo

#13
Y la explicación es?

1 respuesta
Lecherito

#14 con #5 y #7 te deberia dar para buscar (especialmente el segundo que es el verdadero debate)

wasiflo

Pero (y desde el punto de vista de alguien que esta aprendiendo) si yo creo un proyecto nuevo y necesito añadir dependencias de otros que ya tengo, ademas de aparecer en el xml, los objetos y sus métodos aparecen en la clase abstracta. Luego en la interfaz solo tengo la definición del método principal.
Por eso decía que en abstract aparecen las dependencias y así se limpia un poco más el código. Si no es así algo estoy haciendo mal 😁.

2 respuestas
ckrass

#16 🤦

Querosvan

#16 no pensaba contestarte, pero... dónde estás aprendiendo eso así?

para #1 , como dicen, intenta ver la diferencia buscando las referencias que te han dado, pero rara vez necesitarás una abstracta hoy en día

1 respuesta
overflow

#10 Donde me colé fue en lo de ser instanciables. Tenía en mente un código, pero no lo interpreté correctamente... ya hace años que no me muevo por esos terrenos. Para más info: https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html

widim

Muchas gracias a todos por las respuestas, ya me ha quedado clara la diferencia y voy a hacer ahora algunas pruebas para terminar de comprenderlas.

Respecto a lo de composición vs herencia, ¿créeis que es viable/recomendable ir full composición y olvidarse de la herencia?

La verdad que desde que empecé a mirar OOP nunca le he visto demasiado sentido a la herencia (a día de hoy claro, igual para encontrarle el sentido debería irme 30 años atrás), la única ventaja que leo es la de reducir la repetición de código y evitar crear dos o más clases casi clónicas, pero según creo yo, esta ventaja se queda en nada cuando por otro lado estás creando una jerarquía demasiado compleja de clases difícil de mantener, cuyas subclases el día menos pensado pueden petar con facilidad si necesitas hacer algún cambio crítico en la clase padre. Yo creo que la herencia es viable cuando tienes sólo una subclase por clase padre, cuando comienzas a meterle más de dos... malo.

Perdón por el tocho, pero me gustaría saber si mis conclusiones van por el buen camino xD

2 respuestas
desu
#20widim:

Respecto a lo de composición vs herencia, ¿créeis que es viable/recomendable ir full composición y olvidarse de la herencia?

Si es lo habitual hoy en día. Pero como te he dicho arriba, si estas estudiando haz lo que te diga el profesor... Y luego ya aprenderás a programar de verdad.

#20widim:

La verdad que desde que empecé a mirar OOP nunca le he visto demasiado sentido a la herencia (a día de hoy claro, igual para encontrarle el sentido debería irme 30 años atrás), la única ventaja que leo es la de reducir la repetición de código y evitar crear dos o más clases casi clónicas, pero según creo yo, esta ventaja se queda en nada cuando por otro lado estás creando una jerarquía demasiado compleja de clases difícil de mantener.

Correcto, los lenguajes modernos de hecho ya no llevan OOP ni usan jerarquías. No hace falta. Y son malas practicas.

Vas por buen camino.

Hay gente que lleva +5 a;os programando, algunos incluso décadas, y aun no ha entendido porque las jerarquías y la sobre ingeniera están mal. De hecho se enorgullecen de esto porque así parecen mas listos.

No caigas en estos pozos de ego y pajeetismo.

"An idiot admires complexity, while a genius appreciates simplicity"

1
Kaledros
#20widim:

el día menos pensado pueden petar con facilidad si necesitas hacer algún cambio crítico en la clase padre

#20widim:

evitar crear dos o más clases casi clónicas

Si tienes que hacer un cambio crítico porque tu definición no ha previsto algún caso de uso en particular estás jodido de todas formas, tengas composición, herencia o las dos cosas. Da igual cambiar una clase y parchear 25 subclases que cambiar 26 clases.

wasiflo

#18 lo he aprendido en mi trabajo. Se usa así y funciona. Por eso comentaba que es información que le puede servir a #1.
Pero la verdadera cuestión es: dónde has aprendido tu, y el resto que postea, que esa lógica no es posible o está mal?

2 respuestas
Ranthas

#23 Lo que has descrito se parece peligrosamente a un god object.

1 respuesta
Querosvan

#23 que funcione y que lo uses así no define lo correcto.

si es lo que dice #24 , una clase que lo que tiene es todas las inyecciones de las dependencias del proyecto y así puedes usarla donde sea y es una clase de la que extienden todas las demás o algo así, no es la manera de hacerlo. eso o te has explicado mal con lo de "los objetos y sus metodos aparecen en la clase abstracta"

igual algún ejemplo nos hace salir de dudas

Kaledros

Incidiendo en el tema del hilo ya que la duda se ha resuelto, no entiendo muy bien lo que dice #5 de que las interfaces casi han sustituido a las clases abstractas. Me explico.

Imagina que tengo un sistema de servicios que se comunican por mensajería. Y que tengo una clase controller en cada servicio que produce mensajes y se queda a la espera de recibir respuesta (de manera asíncrona). Tengo una interfaz IController que declara el método onMessage(), de manera que implemento IController, sobreescribo onMessage() y arreando.

PERO.

Imagina que cada controller es un bean de Spring que se crea cuando se levanta la aplicación y cada controller tiene en su constructor la manera de declarar los exchanges, colas y topics. Es decir, cada controller se configura cuando la aplicación arranca. Esa lógica es común a todos los controladores, todos se configuran igual y sólo cambian las properties.

En ese caso entiendo que una interfaz se me queda corta porque no puedo inyectarle nada, mientras que a una clase abstracta sí. Es decir, que tendría:

public interface IController {
  void onMessage();
}
 public abstract class AbstractController implements IController {

  private ControllerProperties properties;

  public AbstractController(ControllerProperties properties) {
    //Se configura el controlador usando las properties
  }
  
@Override protected abstract void onMessage(); }
 @Controller
public KaledrosController extends AbstractController {

  public Controller(ApplicationContext context) {
    super(context.getBean(KaledrosProperties.class)); //Imagina que KaledrosProperties extiende de ControllerProperties porque cada controller tiene unas properties.
  }

  @Override
  protected void onMessage() {
    //Lo que sea
  }
}

¿O me he perdido algo?

1 respuesta
mrbeard

Utiliza interfaces, entiéndelas como un contrato que se aplica a una clase y les dice que tienen que hacer.

Además de esto siempre debes depender de abstracciones y no de concreciones.

Lee sobre SOLID y busca ejemplos en un lenguaje de programación que te sientas cómodo si eso te ayuda a entenderlo mejor.

Ranthas

#26 Eso mismo que has puesto, se puede hacer con interfaces a partir de Java 1.8

Tu punto es que AbstractController hace la inicialización común usando el objeto ControllerProperties. Por tanto, en KaledrosController, recibes tu KaledrosProperties, llamas a super para que haga la inicialización común, y a continuación, haces la inicialización propia de KaledrosController. Hasta aquí ok.

Pero fíjate que puedes hacer exactamente lo mismo añadiendo el método configure(ControllerProperties properties) a una interfaz IConfigurer (o lo que sea) con la keyword default. AbstractController implementa IConfigurer y sus hijos, IController. No obstante, este enfoque obliga al desarrollador a conocer que cada hijo de AbstractController debe implementar IController para poder enviar mensajes; con tu ejemplo, debe implementarlo obligatoriamente, lo sepa o no.

El ejemplo se puede hacer de ambas maneras, aunque con la clase abstracta, en este caso, está mejor hecho. Pero ahora planteate que esa inicialización común que haces en AbstractController, cambia o se ramifica. Vas a tener que emplear una nueva estructura de herencia, modificar los controladores base, etc. Usando composición, puedes resolver ese problema de manera más sencilla al tener la lógica de la inicialización separada en interfaces que puedes quitar o añadir sin esfuerzo.

1 1 respuesta
Kaledros
#28Ranthas:

Pero ahora planteate que esa inicialización común que haces en AbstractController, cambia o se ramifica. Vas a tener que emplear una nueva estructura de herencia, modificar los controladores base, etc

Esto es cierto y me ha pasado esta misma semana, he tenido que ramificar una funcionalidad en dos (mala planificación por nuestra parte) para pasar un parámetro más que sólo iban a utilizar un tipo de controladores y me ha tocado bifurcar interfaces y clases abstractas y encima crear una interfaz padre para polimorfismo, un jaleo. Y de hecho es un parche, la semana que viene quiero darle una vuelta y esto me viene bien para pensar en ello.

r2d2rigo

Los de siempre (que no han tirado una linea de codigo decente en su vida, todo sea dicho) diciendo que pa que vas a usar clases abstractas, que eso es compilcarte la vida.

Señor baja y llevame pronto.

1

Usuarios habituales

  • Kaledros
  • Ranthas
  • wasiflo
  • desu
  • overflow
  • Lecherito
  • Leos