2016-11-11 3 views
0

Если класс Java имеет поле, которое инициализируется лениво или по требованию, как мы можем гарантировать, что доступ к ленивому полю осуществляется через его метод инициализации доступа?В Java, как мы защищаем доступ к ленивым полям?

В контексте контекста у нас недавно возникла ситуация, когда разработчик добавил доступ к объекту, который был инициализирован лениво, но не через его метод доступа к инициализации. Это не было обнаружено при компиляции или в модульных тестах, но затем вызвало ошибки времени выполнения.

Например, в следующем SSCCE, _lazyObject инициализируется методом getLazyObject(). Однако, если есть другие методы (в классе, поскольку у него уже есть модификатор доступа private), который хотел бы использовать _lazyObject, нам нужно получить доступ с помощью метода getLazyObject(), так как иначе он, возможно, не был инициализирован.

public class MyObject { 

    private transient volatile Object _lazyObject; 

    public Object getLazyObject() { 
    if (_lazyObject == null) { 
     synchronized (this) { 
     if (_lazyObject == null) { 
      _lazyObject = new Object(); 
     } 
     } 
    } 
    return _lazyObject; 
    } 

    public void doSomething() { 
    Object a = _lazyObject; // may be null - will compile, but may cause runtime errors! 
    Object b = getLazyObject(); // subject to exceptions, will not be null - this is how it should be accessed. 
    // do something... 
    } 
} 

Как мы можем гарантировать, что доступ _lazyObject осуществляется через getLazyObject()?

  • Возможно ли это в кодексе в пределах MyObject?
  • В качестве альтернативы можно ли это обеспечить посредством модульных испытаний?
+0

Вы могли бы включать в себя утверждение, чтобы проверить '_lazyObject' был экземпляр' утверждать _lazyObject = нуль! "! Используйте getLazyObject()" 'для целей модульного тестирования , Я предполагаю, что у MyObject есть частный конструктор, чтобы другие классы не запускали экземпляр MyObject, хотя это не то, о чем ваша проблема. –

+0

Является ли ленивый объект действительно типом объекта или он более конкретный? – Gimby

+0

@Gimby - это всегда более конкретный. Мы используем ленивую инициализацию довольно широко - особенно при сериализации классов/совместном использовании в JVM, поэтому обычно это нечто слишком большое, чтобы сериализовать (т. Е. Быстрее инициализировать) или что-то, что не может быть сериализовано. – amaidment

ответ

0

Хорошо, поэтому я открыт для дальнейших предложений, но это лучшее решение, которое я придумал до сих пор.

Мы можем «защитить» ленивые переменные в объекте INITIALIZING - подумал я про это пишет сам, но обнаружил, что есть хорошие реализации этого в Apache Commons Lang (LazyInitializer) и Google гуаве (Supplier). (. Кредит Kenston Choi's answer to this question)

Например - для уточнения, я изменил ленивый класс объектов из Object в заполнитель T:

public class MyObject { 

    private transient Supplier<T> _lazyObject = Suppliers.memoize(new Supplier<T>() { 
    @Override 
    public T get() { 
     return ...; // make T 
    } 
    }); 

    public T getLazyObject() { 
    return _lazyObject.get(); 
    } 

    public void doSomething() { 
    Supplier<T> a = _lazyObject; // a is actually the Supplier 
    // ... but we can access either via the method 
    T b = getLazyObject(); 
    // or the Supplier: 
    T c = _lazyObject.get(); 
    // do something... 
    } 
} 

Однако, в соответствии с комментариями выше - один из моих главных использование случаев - сериализация/де-сериализация объектов, содержащих ленивые поля в JVM. В этом случае после де-сериализации Supplier будет null. Таким образом, мы должны инициализировать Supplier после десериализации.

Например, с помощью most simple approach:

public class MyObject { 

    private transient Supplier<T> _lazyObject = makeSupplier(); 

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 
    in.defaultReadObject(); 
    _lazyObject = makeSupplier(); 
    } 

    private Supplier<T> makeSupplier() { 
    return Suppliers.memoize(new Supplier<T>() { 
     @Override 
     public Tget() { 
     return ...; // make T 
     } 
    }); 
    } 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^