Можно ли изменить начальное значение поля по умолчанию с помощью отражения в Java? - PullRequest
0 голосов
/ 29 августа 2018

Допустим, у нас есть класс с полем, и у него есть начальное значение по умолчанию, которое не изменяется конструктором, например

public class Server {
  private int pingFrequency = 500;

  public Server() {
  }
}

Теперь я не хочу менять начальное значение по умолчанию на другое значение ДО объект построен. Причина в том, что этот класс используется библиотекой и скрывает экземпляр объекта. Так что я мог контролировать только когда объект построен, но не где и как.

Я пытался получить поле с помощью отражения, но я не вижу способа изменить значение по умолчанию

Field pingFrequency =  Class.forName("Server").getDeclaredField("pingFrequency")

Я думаю, что должен что-то изменить в загрузчике классов, но я не знаю, что и как.

Спасибо

1 Ответ

0 голосов
/ 31 августа 2018

Когда вы объявляете класс как

public class Server {
  private int pingFrequency = 500;

  public Server() {
  }
}

ничем не отличается от

public class Server {
  private int pingFrequency;

  public Server() {
    pingFrequency = 500;
  }
}

или

public class Server {
  private int pingFrequency;

  {
    pingFrequency = 500;
  }
  public Server() {
  }
}

Фактически, все три варианта компилируются в один и тот же байт-код. Код всех инициализаторов полей и блоков инициализатора экземпляра копируется в каждый конструктор этого класса, прямо между вызовом супер-конструктора и остальной частью конструктора. [ ПСБ §12.5 ]
¹, который не делегирует другому конструктору этого класса

Единственный способ изменить присвоенное значение - изменить код всех конструкторов, чтобы изменить присвоение. Это не может быть сделано с помощью Reflection, но только с помощью инструментов манипулирования байт-кодом.

Обратите внимание, что когда поле было объявлено final, как

public class Server {
  private final int pingFrequency = 500;

  public Server() {
  }
}

в байт-коде будет атрибут, сообщающий постоянное значение [ JVMS §4.7.2 ], дополнительно к назначению. Однако для такой постоянной времени компиляции каждый обычный доступ на чтение будет заменен постоянным значением во время компиляции [ JLS §13.1 ], поэтому даже изменение назначения не будет иметь никакого эффекта в этом случае (и при этом не будет изменение атрибута) [ JLS §13.4.9 ]. Попытка заменить фактическое использование поля создаст проблему, заключающуюся в том, что вы не сможете отличить их от других вариантов использования постоянного числа 500.

Если бы поле было static и final, присваивания не было вообще, атрибут константного значения использовался бы для инициализации поля, однако изменение его имело бы такой же небольшой эффект, как и для поля экземпляра константы, поскольку доступ к полю все еще был заменен старым постоянным значением.

...