Come mutare un campo privato dalla classe astratta in Java?
-
10-07-2019 - |
Domanda
Esiste una classe astratta:
public abstract class AbstractAny {
private long period;
public void doSomething() {
// blah blah blah
period = someHardcodedValue;
// blah blah blah
}
}
Non voglio cambiare l'origine della classe astratta ma devo aggiungere una certa flessibilità su come viene impostato il periodo di campo. È possibile modificare il valore del periodo di campo da un metodo di sostituzione? Come ad esempio:
public class ConcreteSome extends AbstractAny{
@Override
public void doSomething() {
try {
Field p = super.getClass().getDeclaredField("period");
p.setAccessible(true);
p.setLong(this, 10L);
} catch (SecurityException e) {
throw new RuntimeException(e);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
Quando provo a eseguire questo codice super.getClass (). getDeclaredField (" period ")
genera java.lang.NoSuchFieldException: period
Soluzione
È necessario getClass (). getSuperclass ()
per ottenere la superclasse, non super.getClass ()
.
Tuttavia, davvero non consiglio di farlo. Stai praticamente distruggendo l'incapsulamento della classe astratta. Se la classe astratta non fornisce abbastanza flessibilità per i suoi discendenti, dovrebbe essere riparata - aggirarla è solo chiedere problemi. Cosa succede se qualche altro membro della classe astratta deve essere cambiato ogni volta che lo fa? Il punto fondamentale di avere uno stato privato è che la classe sia in grado di proteggere i propri dati. Usare la riflessione in questo modo è un brutto trucco che dovrebbe essere un ultimo ricorso assoluto.
Altri suggerimenti
Sono con Jon Skeet. A lungo termine è più semplice modificare il codice sorgente della superclasse per rendere questo campo protetto o delegare la mutazione a un metodo sostituibile. L'uso di reflection per modificare il valore ora funziona correttamente, ma non si adatta molto bene alla manutenzione nel tempo.
ConcreteSome non estende la classe astratta AbstractAny.
Prova questo
public class ConcreteSome {
@Override
public void doSomething() {
Class<?> clazz = getClass().getSuperclass();
System.out.println(clazz);
Field p = clazz.getDeclaredField("period");
...
dovrebbe restituire java.lang.Object