Comment muter un champ privé de la classe abstraite en Java?
-
10-07-2019 - |
Question
Il existe une classe abstraite:
public abstract class AbstractAny {
private long period;
public void doSomething() {
// blah blah blah
period = someHardcodedValue;
// blah blah blah
}
}
Je ne souhaite pas changer la source de la classe abstraite, mais je dois ajouter une certaine flexibilité dans la définition de la période de champ. Est-il possible de changer la valeur de la période sur le terrain d'une méthode surchargée? Comme par exemple:
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);
}
}
}
Lorsque j'essaie d'exécuter ce code super.getClass (). getDeclaredField ("période")
lève java.lang.NoSuchFieldException: period
La solution
Vous avez besoin de getClass (). getSuperclass ()
pour obtenir la superclasse, et non de super.getClass ()
.
Cependant, je vraiment ne recommande pas de faire cela. Vous détruisez fondamentalement l'encapsulation de la classe abstraite. Si la classe abstraite n'offre pas suffisamment de flexibilité à ses descendants, elle doit être corrigée - la contourner, c'est simplement poser des problèmes. Que faire si un autre membre de la classe abstraite doit être changé chaque fois que celui-ci le fait? L'intérêt d'avoir un état privé est de permettre à la classe de protéger ses propres données. Utiliser une réflexion comme celle-ci est un vilain bidon qui devrait être un dernier recours absolu.
Autres conseils
Je suis avec Jon Skeet. À long terme, il est plus facile de modifier le code source de la superclasse afin de protéger ce champ ou de déléguer la mutation à une méthode pouvant être remplacée. L'utilisation de la réflexion pour modifier la valeur fonctionne bien à présent, mais elle ne s'adapte pas très bien en termes de maintenance.
ConcreteSome n'étend pas la classe abstraite AbstractAny.
Essayez ceci
public class ConcreteSome {
@Override
public void doSomething() {
Class<?> clazz = getClass().getSuperclass();
System.out.println(clazz);
Field p = clazz.getDeclaredField("period");
...
doit renvoyer java.lang.Object