2016-08-25 2 views
0

У меня есть библиотека, которую я должен использовать, у которой есть опасная инициализация статического значения (для примера классы были сокращены до минимума):- это FindBugs 'JLM_JSR166_UTILCONCURRENT_MONITORENTER', безопасный для игнорирования в этом случае

public TheirBaseClass { 
    public static String PathToUse = null; 

    public BaseClass(){ 
     PathToUse = "Configured"; 

     // ... 
     // do some other stuff with other side effects 
     // ... 
    } 
} 

У меня есть случай, когда я пытаюсь читать значение ConfigValue без создания экземпляра класса (чтобы избежать некоторых побочных эффектов).

Paths.get(TheirBaseClass.PathToUse).toFile().... 

Это вызывает NullPointerException

Потому что я должен использовать этот класс, я ищу, чтобы наследовать от него, и попытаться принять меры, чтобы гарантировать, что инициализация имеет место при доступе к статическим.

public MyBaseClass extends TheirBaseClass{ 

    private static final AtomicBoolean isInitialized = new AtomicBoolean(false); 

    static { 
     MyBaseClass.Initialize(); 
    } 

    public static void Initialize(){ 
     // FindBugs does not like me synchronizing on a concurrent object 
     synchronized(isInitialized){ 
      if(isInitialized.get()){ 
       return; 
      } 
      new TheirBaseClass(); 
      isInitialized.set(true); 
     } 
    } 

    public MyBaseClass(){ 
     super(); 
    } 

} 

Который позволяет мне

MyBaseClass.Initialize(); 
Paths.get(MyBaseClass.PathToUse).toFile().... 

Это, кажется, работает достаточно хорошо (и устраняет некоторые другие дефекты фантомные мы имели). Он позволяет TheirBaseClass функционировать естественным образом, позволяя мне безопасно принудительно инициализировать в нескольких случаях, которые мне могут понадобиться.

Однако, когда я запускаю FindBugs против этого кода, я получаю JLM_JSR166_UTILCONCURRENT_MONITORENTER. После прочтения описания, я согласен, что использование AtomicBoolean может быть опасно, потому что кто-то может изменить значение, но ...

  1. Я думаю, что его можно проигнорировать в этом случае (но есть сомнения)
  2. Я вообще предпочитаю переписать код, чем поставить маркер игнорирования на место

Я действительно делаю что-то опасное (и просто не вижу его)? Есть лучший способ сделать это?

К сожалению, использование другого TheirBaseClass не является вариантом.

Связанные

ответ

1

Вы можете найти его проще адаптировать lazy holder idiom:

public MyBaseClass { 
    private static class Initializer { 
    static { 
     new TheirBaseClass(); 
    } 

    // Doesn't actually do anything; merely provides an expression 
    // to cause the Initializer class to be loaded. 
    private static void ensureInitialized() {} 
    } 

    { 
    Initializer.ensureInitialized(); 
    } 

    // Rest of the class. 
} 

Это использует тот факт, что загрузка класса происходит только один раз и синхронизируется (в пределах одного загрузчика классов). Это происходит только при создании экземпляра MyBaseClass.

+0

Я думаю, что я сделал большую ошибку в моем примере: я не расширил 'ИхBaseClass' –

+0

Мне пришлось обдумать это. Я уже внедряю «ленивый держатель». Ваш ответ верный, но путаница, кажется, возникает вокруг логического: это совершенно необходимо. Я представил это для конкретного случая, но этот случай, похоже, ушел с большим количеством рефакторинга. –

+0

Boolean не нужно * если я делаю инициализатор приватным * –