2010-04-30 17 views
13

En .NET, se puede especificar un atributo "mustoverride" a un método en una determinada superclase para garantizar que las subclases anulen ese método en particular. Me preguntaba si alguien tiene una anotación Java personalizada que podría lograr el mismo efecto. Esencialmente, lo que quiero es presionar para que las subclases anulen un método en una superclase que tiene una lógica que debe ser ejecutada. No quiero utilizar métodos abstractos o interfaces, porque quiero que se ejecute alguna funcionalidad común en el método super, pero más o menos produzca un error/advertencia del compilador que indique que las clases derivadas deben anular un método determinado.@MustOverride anotación?

+2

Si anula un método en Java, ya no se llamará al método en la superclase, a menos que tenga un "super" explícito. Entonces, no creo que tu idea (forzar la ejecución de un método de superclase común) funcionaría, incluso si existiera tal atributo. – Carsten

Respuesta

12

Ignorando los métodos abstractos, no existe tal facilidad en Java. Tal vez sea posible crear una anotación en tiempo de compilación para forzar ese comportamiento (y no estoy seguro de que lo sea), pero eso es todo.

El verdadero pateador es "anular un método en una superclase que tiene una lógica que debe ejecutarse". Si anula un método, no se llamará al método de la superclase a menos que lo llame explícitamente.

En este tipo de situaciones que he tendido a hacer algo como:

abstract public class Worker implements Runnable { 
    @Override 
    public final void run() { 
    beforeWork(); 
    doWork(); 
    afterWork(); 
    } 

    protected void beforeWork() { } 
    protected void afterWork() { } 
    abstract protected void doWork(); 
} 

para forzar una estructura lógica particular sobre el método de una interfaz. Usted podría utilizar esto, por ejemplo, para contar invocaciones sin tener que preocuparse acerca de si el usuario llama super.run(), etc.

5

... y si se declara una clase base abstract no es una opción siempre se puede tirar una UnsupportedOperationException

class BaseClass { 
    void mustOverride() { 
     throw new UnsupportedOperationException("Must implement"); 
    } 
} 

Pero esto no es un cheque en tiempo de compilación, por supuesto ...

4

no estoy seguro de qué atributo está pensando en .NET.

En VB puede aplicar el modificador MustOverride a un método, pero eso es solo el equivalente a hacer el método abstracto en Java. No necesita un atributo/anotación, ya que el concepto está integrado en los idiomas. Es más que solo aplicar metadatos; también existe la diferencia crucial de que un método abstracto no incluye ninguna implementación en sí misma.

Si do cree que hay tal atributo, ¿podría decir a cuál se refiere?

19

No entiendo muy bien por qué no desea usar el modificador abstracto: esto está destinado a forzar la implementación por subclase, y solo debe usarse para algunos métodos, no para todos. ¿O tal vez estás pensando en clases "abstractas puras" al estilo C++?

Pero otra cosa que muchos desarrolladores de Java no conocen es que también es posible anular los métodos no abstractos y declararlos abstractos; como:

public abstract String toString(); // force re-definition 

por lo que a pesar de java.lang.Object ya define una implementación, puede forzar subclases de definirlo nuevamente.

+0

Haga que el método sea abstracto. Esta debería ser la respuesta aceptada. –

+2

Hay muchas ocasiones en que uno puede desear tener lógica base en el supermétodo pero tener un recordatorio para futuros desarrolladores que sin extender esta lógica y llamar a super(), el niño no puede esperar un comportamiento normal según las necesidades del negocio. – Angad

+0

Como un ejemplo de esto, la mayoría de las veces usamos BaseClass como está. BaseClass tiene una función de copia que devuelve una copia profunda. Llamar a ChildClass.copy() debería devolver una nueva instancia de ChildClass, no la clase base. Me gustaría aplicar la anulación en tiempo de compilación para evitar errores sutiles en la producción donde 1 o 2 campos no se copian y devuelven 0. –

1

Si necesita un comportamiento predeterminado, pero por alguna razón, no debe ser utilizado por especializaciones, como una implementación de una lógica en una clase de adaptador no abstracta solo para crear prototipos, pero que no debe utilizarse en producción, por ejemplo , podría encapsular esa lógica y registrar una advertencia de que se está utilizando, sin tener que ejecutarla.

El constructor de la clase base podría verificar si la variable que contiene la lógica apunta a la predeterminada. (Escrito en términos muy abstractos como creo que debería funcionar en cualquier idioma)

que sería algo como esto (sin compilar, no probado e incompleta) de Java (hasta 7) ejemplo:

public interface SomeLogic { 
void execute(); 
} 

public class BaseClass { 

    //...private stuff and the logging framework of your preference... 

    private static final SomeLogic MUST_OVERRIDE = new SomeLogic() { 
    public void execute() { 
     //do some default naive stuff 
    } 
    }; 

    protected SomeLogic getLogic() { return MUST_OVERRIDE; } 

    //the method that probably would be marked as MustOverride if the option existed in the language, maybe with another name as this exists in VB but with the same objective as the abstract keyword in Java 
    public void executeLogic() { 
    getLogic().execute(); 
    } 

    public BaseClass() { 
    if (getLogic() == MUST_OVERRIDE) { 
     log.warn("Using default logic for the important SomeLogic.execute method, but it is not intended for production. Please override the getLogic to return a proper implementation ASAP"); 
    } 
    } 

} 

public GoodSpecialization extends BaseClass { 

    public SomeLogic getLogic() { 
    //returns a proper implementation to do whatever was specified for the execute method 
    } 

    //do some other specialized stuff... 
} 

public BadSpecialization extends BaseClass { 

    //do lots of specialized stuff but doesn't override getLogic... 
} 

Algunas cosas podría ser diferente según los requisitos, y claramente más simple, especialmente para lenguajes con expresiones lambda, pero la idea básica sería la misma.

Sin la cosa incorporada, siempre hay alguna forma de emularla, en este ejemplo obtendría una advertencia de tiempo de ejecución en un archivo de registro con una solución similar a un patrón hecho en casa, que solo sus necesidades deberían señalar si es suficiente o una manipulación de bytecode más duradera, desarrollo de complemento ide o cualquier magia que se necesite.

2

Android tiene una nueva anotación a cabo como se anunció en el Google I/O 2015: @callSuper

Más detalles aquí: http://tools.android.com/tech-docs/support-annotations

+3

+1 para la utilidad, pero no responde la pregunta. Necesitamos una anotación que advierta a la subclase diciendo 'Debes anular este método' – Angad

-1

He estado pensando en esto.

Si bien no conozco ninguna forma de exigirlo con un error de compilación, puede intentar escribir una regla de PMD personalizada para levantar una bandera roja si se olvidó de anularla.

Ya hay muchas reglas de PMD que hacen cosas como recordarle que implemente HhashCode si elige anular iguales. Quizás algo podría hacerse así.

Nunca he hecho esto antes, así que no soy de los que escribo un tutorial, pero un buen lugar para comenzar sería este enlace http://techtraits.com/programming/2011/11/05/custom-pmd-rules-using-xpath/ En este ejemplo, básicamente crea una pequeña advertencia si decides usar un comodín en un paquete de importación. Úselo como punto de partida para explorar cómo PMD puede analizar su código fuente, visitar a cada miembro de una jerarquía e identificar dónde olvidó implementar un método específico.

Las anotaciones también son una posibilidad, pero tendrías que encontrar tu propia forma de implementar la navegación a través de la ruta de clase. Creo que PMD ya maneja esto. Además, PMD tiene una muy buena integración con IDEs. https://pmd.github.io/