Wie ein privates Feld von der abstrakten Klasse in Java mutieren?
-
10-07-2019 - |
Frage
Es gibt eine abstrakte Klasse:
public abstract class AbstractAny {
private long period;
public void doSomething() {
// blah blah blah
period = someHardcodedValue;
// blah blah blah
}
}
Ich will nicht die Quelle der abstrakten Klasse ändern, aber hinzufügen muß eine gewisse Flexibilität auf, wie die Feldperiode festgelegt wird. Ist es möglich, den Wert der Feldperiode von einer überschriebenen Methode zu ändern? Wie zum Beispiel:
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);
}
}
}
Wenn ich versuche, diesen Code auszuführen super.getClass().getDeclaredField("period")
java.lang.NoSuchFieldException: period
wirft
Lösung
Sie müssen getClass().getSuperclass()
die übergeordnete Klasse zu bekommen, nicht super.getClass()
.
Allerdings I wirklich Sie tun dies nicht empfehlen. Sie zerstören im Grunde die Kapselung der abstrakten Klasse. Wenn die abstrakte Klasse ist nicht genug Flexibilität für seine Nachkommen bereitstellt, sollte es festgelegt werden - geht um es nur Ärger bringen wird. Was passiert, wenn ein anderes Mitglied der abstrakten Klasse muss geändert werden, wann immer dies tut? Der ganze Sinn der privaten Zustand aufweist, so dass die Klasse in der Lage ist, seine eigenen Daten zu schützen. Mit Reflexion wie dies eine wirklich hässliche Hack ist, die eine absolute letzte Mittel sein sollte.
Andere Tipps
ich mit Jon Skeet bin. Es ist einfacher, auf lange Sicht zu den Source-Code der übergeordneten Klasse ändern entweder dieses Feld machen geschützt oder Mutation zu einer überschreibbare Methode zu delegieren. Mit Reflexion des Wert ändern jetzt funktioniert ok, aber es nicht sehr gut skalieren bis Wartung im Laufe der Zeit geht.
ConcreteSome erweitert nicht die abstrakte Klasse AbstractAny.
Versuchen Sie, diese
public class ConcreteSome {
@Override
public void doSomething() {
Class<?> clazz = getClass().getSuperclass();
System.out.println(clazz);
Field p = clazz.getDeclaredField("period");
...
sollte java.lang.Object zurückkehren