2010-02-23 14 views
14

Existe una posible optimización que podría aplicar a uno de mis métodos, si puedo determinar que otro método en la misma clase no se reemplaza. Es solo una ligera optimización, por lo que la reflexión está fuera de cuestión. ¿Debo simplemente hacer un método protegido que devuelva si el método en cuestión está anulado, de modo que una subclase puede hacer que devuelva verdadero?Cómo determinar rápidamente si un método se reemplaza en Java

+2

¿Qué tal que no optimizan? Parece que la optimización no es lo suficientemente significativa como para realmente importar. –

+0

Anon: El método se llama cientos de veces por segundo. – bgw

+0

Luego busque reflexión y guarde el resultado en un campo privado. –

Respuesta

29

No haria esto. Viola la encapsulación y cambia el contrato de lo que su clase debe hacer sin que los implementadores lo sepan.

Si debe hacerlo, sin embargo, la mejor manera es invocar

class.getMethod("myMethod").getDeclaringClass(); 

Si la clase que se devuelve es su propia, entonces no es anulado; si es otra cosa, esa subclase lo ha anulado. Sí, esto es reflejo, pero sigue siendo bastante barato.

Aunque me gusta su enfoque de método protegido. Eso sería algo como esto:

public class ExpensiveStrategy { 
    public void expensiveMethod() { 
    // ... 
    if (employOptimization()) { 
     // take a shortcut 
    } 
    } 

    protected boolean employOptimization() { 
    return false; 
    } 
} 

public class TargetedStrategy extends ExpensiveStrategy { 
    @Override 
    protected boolean employOptimization() { 
    return true; // Now we can shortcut ExpensiveStrategy. 
    } 
} 
+0

Bueno, mi optimización es un rendimiento pequeño caso por caso, y solo acelera mucho las cosas porque se llama cientos de veces por segundo. Con la reflexión, ese rendimiento se reduciría a tal punto que la optimización no tiene sentido. Una buena respuesta, así que estoy votando esto de todos modos. – bgw

+0

Si realmente lo necesita, puede determinar si los métodos están sobrecargados a través de un inicializador estático y almacenar el resultado en un booleano. Alternativamente, puede agregar esto a través de aspect0 – vickirk

+0

Use Class.getMethod en su lugar. Class.getDeclaredMethod elevará NoSuchMethodException si pregunta sobre un método heredado que la subclase no anula. Por supuesto, podría usar la excepción como indicador (deficiente) de que el método no fue anulado. –

2

¿Cuántas veces espera que se llame a la función durante la vida útil del programa? La reflexión sobre un único método específico no debería ser tan malo. Si no vale tanto tiempo durante la vida útil del programa, mi recomendación es mantenerlo simple y no incluir la pequeña optimización.

Jacob

+0

Es un método muy comúnmente llamado. – bgw

+2

Déjame aclarar mi respuesta. Estaba sugiriendo que si tiene este patrón común, y las instancias son todas del mismo tipo, podría almacenar en caché el valor de la reflexión que se realiza una vez. Usted determina la configuración la primera vez y usa ese valor en todo momento. De esta manera, la sobrecarga de reflexión sería una vez y las llamadas serían un número mucho mayor. Si esto se llama repetidamente en cada instancia, una variable de instancia declarada en la clase padre e inicializada en la primera llamada (usando la reflexión) podría darle un impulso. – TheJacobTaylor

0

La reflexión se puede utilizar para determinar si un método se reemplaza. El código es un poco complicado. Por ejemplo, debe tener en cuenta que tiene una clase de tiempo de ejecución que es una subclase de la clase que anula el método.

Vas a ver las mismas clases de tiempo de ejecución una y otra vez. Para que pueda guardar los resultados del cheque en un WeakHashMap ingresado en el Class.

Vea mi código en java.awt.Component que trata con coalesceEvents para un ejemplo.

+0

que no puedo hacer, el valor cambia ligeramente cada vez. – bgw

+0

@PiPeep: la instancia real es irrelevante. Como digo, la clave del mapa se basa en la clase de tiempo de ejecución. –

1

Anotar subclases que anulan el método particular. @OverridesMethodX.

Realice el trabajo reflector necesario en la carga de clase (es decir, en un bloque static) para que publique la información a través de un indicador booleano final. Luego, consulta la bandera donde y cuando la necesites.

+1

bloques estáticos rompen la herencia. – bgw

+0

Sé que huele. Pero la optimización que desea ya está subvirtiendo el polimorfismo. –

7

Bueno, mi optimización es un rendimiento pequeño caso por caso, y solo acelera mucho las cosas porque se llama cientos de veces por segundo.

Es posible que desee ver lo que el optimizador de Java puede hacer. Su optimización codificada a mano puede no ser necesaria.

Si decide que la optimización codificada a mano es necesaria, el método de método protegido que describió no es una buena idea porque expone los detalles de su implementación.

+0

+1 Me parece un buen consejo, no sé por qué no votó. – vickirk

1

quizás haya una manera más clara de hacerlo a través del Strategy Pattern, aunque no sé cómo se modelan el resto de la aplicación y los datos, pero parece que podría encajar.

Me hizo de todos modos cuando me enfrentaba con un problema similar.Podría tener una heurística que decida qué estrategia utilizar dependiendo de los datos que se procesarán.

De nuevo, no tengo suficiente información sobre su uso específico para ver si esto es excesivo o no. Sin embargo, me abstendría de cambiar la firma de clase para dicha optimización específica. Por lo general, cuando siento la necesidad de ir en contra de la corriente, interpreto que no había imaginado un caso de esquina cuando diseñé el producto y que debería refactorizarlo para obtener una solución más completa y más limpia.

Sin embargo, tenga en cuenta que tal refactorización cuando se realiza únicamente en terrenos de optimización casi inevitablemente conduce a un desastre. Si este es el caso, tomaría el enfoque reflexivo sugerido anteriormente. No altera el contrato de herencia, y cuando se hace correctamente debe hacerse una sola vez por subclase que lo requiere para la vida útil de la aplicación.

1

Sé que esto es una pregunta un poco viejo, pero por el bien de otros empleados de Google:

me ocurrió una solución diferente utilizando interfaces.

class FastSub extends Super {} 
class SlowSub extends Super implements Super.LetMeHandleThis { 
    void doSomethingSlow() { 
     //not optimized 
    } 
} 
class Super { 
    static interface LetMeHandleThis { 
     void doSomethingSlow(); 
    } 
    void doSomething() { 
     if (this instanceof LetMeHandleThis) 
      ((LetMeHandleThis) this).doSomethingSlow(); 
     else 
      doSomethingFast(); 
    } 
    private final void doSomethingFast() { 
     //optimized 
    } 
} 

o al revés:

class FastSub extends Super implements Super.OptimizeMe {} 
class SlowSub extends Super { 
    void doSomethingSlow() { 
     //not optimized 
    } 
} 
class Super { 
    static interface OptimizeMe {} 
    void doSomething() { 
     if (this instanceof OptimizeMe) 
      doSomethingFast(); 
     else 
      doSomethingSlow(); 
    } 
    private final void doSomethingFast() { 
     //optimized 
    } 
    void doSomethingSlow(){} 
} 
0
private static boolean isMethodImplemented(Object obj, String name) 
{ 
    try 
    { 
     Class<? extends Object> clazz = obj.getClass(); 

     return clazz.getMethod(name).getDeclaringClass().equals(clazz); 
    } 
    catch (SecurityException e) 
    { 
     log.error("{}", e); 
    } 
    catch (NoSuchMethodException e) 
    { 
     log.error("{}", e); 
    } 

    return false; 
} 
+0

Ha publicado exactamente la misma respuesta aquí y [aquí] (http://stackoverflow.com/questions/4821704/java-how-to-find-if-a-method-is-overridden-from-base-class/19637356# 19637356). Si puede dar exactamente la misma respuesta, debe marcar ** la pregunta como duplicada **, ** no la respuesta duplicada ** también. –

Cuestiones relacionadas