una sirve sintaxis que para metodo interfaz interfaces entre ejemplos ejemplo diferencia clases clase abstracto abstractas abstracta abstraccion java polymorphism default abstract-methods

java - sirve - ¿Implementación predeterminada o método abstracto?



para que sirve una clase abstracta (5)

Encontré un escenario similar cuando creé el esquema básico (jerarquía de clases) de una aplicación que estaba desarrollando junto con otros en el trabajo. Mi elección para colocar un método abstracto (y, por consiguiente, forzar su implementación) fue con fines de comunicación .

Básicamente, los otros compañeros de equipo tuvieron que implementar explícitamente el método y, por lo tanto, primero notan su presencia y luego acuerdan qué devuelven allí, incluso si es solo la implementación predeterminada.

Las implementaciones predeterminadas en las clases base a menudo se pasan por alto.

¿Es mejor poner una implementación predeterminada de un método en una superclase y anularla cuando las subclases quieren desviarse de esto, o simplemente debe dejar el método de superclase abstracto y repetir la implementación normal en muchas subclases?

Por ejemplo, un proyecto en el que estoy involucrado tiene una clase que se usa para especificar las condiciones en las que debe detenerse. La clase abstracta es la siguiente:

public abstract class HaltingCondition{ public abstract boolean isFinished(State s); }

Una implementación trivial podría ser:

public class AlwaysHaltingCondition extends HaltingCondition{ public boolean isFinished(State s){ return true; } }

La razón por la que hacemos esto con los objetos es que podemos componer arbitrariamente estos objetos. Por ejemplo:

public class ConjunctionHaltingCondition extends HaltingCondition{ private Set<HaltingCondition> conditions; public void isFinished(State s){ boolean finished = true; Iterator<HaltingCondition> it = conditions.iterator(); while(it.hasNext()){ finished = finished && it.next().isFinished(s); } return finished; } }

Sin embargo, tenemos algunas condiciones de detención que deben notificarse que se han producido eventos. Por ejemplo:

public class HaltAfterAnyEventHaltingCondition extends HaltingCondition{ private boolean eventHasOccurred = false; public void eventHasOccurred(Event e){ eventHasOccurred = true; } public boolean isFinished(State s){ return eventHasOccurred; } }

¿Cómo deberíamos representar mejor el eventHasOccurred(Event e) en la superclase abstracta? La mayoría de las subclases pueden tener una implementación no operativa de este método (por ejemplo, AlwaysHaltingCondition ), mientras que algunas requieren una implementación significativa para funcionar correctamente (por ejemplo, HaltAfterAnyEventHaltingCondition ) y otras no necesitan hacer nada con el mensaje, sino que deben transmitirlo a sus subordinados. para que funcionen correctamente (por ejemplo, ConjunctionHaltingCondition ).

Podríamos tener una implementación predeterminada, que reduciría la duplicación de código, pero causaría que algunas subclases se compilaran pero no funcionaran correctamente si no se anulara, o podríamos declarar el método como abstracto, lo que requeriría que el autor de cada subclase piense en la implementación que estaban proporcionando, aunque nueve veces de cada diez sería una implementación sin operación. ¿Cuáles son las otras ventajas y desventajas de estas estrategias? ¿Es uno mucho mejor que el otro?


Parece que le preocupa configurar esa variable booleana cuando ocurre el evento. Si el usuario reemplaza eventHasOccurred() , entonces la variable booleana no se establecerá y isFinished() no devolverá el valor correcto. Para hacer esto, puede tener un método abstracto que el usuario reemplaza para manejar el evento y otro método que llama al método abstracto y establece el valor booleano (consulte el ejemplo de código a continuación).

Además, en lugar de poner el método eventHasOccurred() en la clase HaltingCondition , puede hacer que las clases que necesitan manejar los eventos extiendan una clase que define este método (como la clase a continuación). Cualquier clase que no necesite manejar eventos solo puede extender HaltingCondition :

public abstract class EventHaltingCondition extends HaltingCondition{ private boolean eventHasOccurred = false; //child class implements this //notice how it has protected access to ensure that the public eventHasOccurred() method is called protected abstract void handleEvent(Event e); //program calls this when the event happens public final void eventHasOccurred(Event e){ eventHasOccurred = true; //boolean is set so that isFinished() returns the proper value handleEvent(e); //child class'' custom code is executed } @Override public boolean isFinished(){ return eventHasOcccurred; } }

EDITAR (ver comentarios):

final EventHaltingCondition condition = new EventHaltingCondition(){ @Override protected void handleEvent(Event e){ //... } }; JButton button = new JButton("click me"); button.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent actionEvent){ //runs when the button is clicked Event event = //... condition.eventHasOccurred(event); } });


Si está dejando el resumen del método de la super clase, puede querer considerar la utilización de una interfaz (no debe confundirse con una interfaz). Como la interfaz es una que no proporciona una implementación concreta.

Scott

Para expandir, cuando los programadores recibimos instrucciones para programar una interfaz a veces nuevas, y en ocasiones con experiencia, los desarrolladores asumirán incorrectamente que se trata de la Interfaz de palabras clave en la que no se pueden encontrar detalles de implementación. Sin embargo, la forma más clara de decir esto es que cualquier objeto de nivel superior puede tratarse como una interfaz con la que se puede interactuar. Por ejemplo, una clase abstracta llamada Animal sería una interfaz que una clase llamada Cat que heredaría de Animal.


Si va a colocar cualquier implementación en la clase base abstracta, debe ser el código para las subclases que usan la implementación sin operación, ya que esta es una implementación que también tiene sentido para la clase base. Si no hubiera una implementación sensata para la clase base (por ejemplo, si no hubiera un no-op sensato para el método que está discutiendo aquí), entonces sugiero dejarlo en abstracto.

Con respecto al código duplicado, si hay "familias" de clases que usan la misma implementación del método y no quiere duplicar el código en todas las clases de la familia, simplemente puede usar clases auxiliares por familia que proporcionen estas implementaciones. En su ejemplo, un ayudante para las clases que transmiten los eventos, un ayudante para las clases acepta y registra el evento, etc.


Una opción es tener otra subclase abstracta, para usar como la superclase para todas las implementaciones que deseen usar la implementación predeterminada.

Personalmente, generalmente dejo los métodos no finales abstractos en una clase abstracta (o simplemente uso interfaces en su lugar) pero definitivamente depende de la situación. Si tiene una interfaz con muchos métodos, y quiere poder simplemente optar por algunos de ellos, por ejemplo, está bien una clase abstracta que implemente la interfaz de una manera no operativa para cada método.

Es necesario evaluar cada caso en sus méritos, básicamente.