Lazy initialization is a performance optimization. It's used when data is deemed to be 'expensive' for some reason. For example:
To avoid a NullPointerException, a class must self-encapsulate fields that have lazy initialization. That is, a class cannot refer directly to such fields, but must access them through a method.
The hashCode method of an immutable Model Object is a common candidate for lazy initialization.
Example 1
In this example, there are two fields with lazy initialization - fHashCode and fAwards.
Here, the look up of the printers available to a desktop PC is treated as an expensive operation.
There are several policies for GUI construction which a design may follow:
- if the hashCode value for an object might not actually be needed by its caller, always calculating the hashCode for all instances of the object may be felt to be unnecessary.
- since accessing a file system or network is relatively slow, such operations should be put off until they are absolutely required.
- delay an expensive operation until it's absolutely necessary
- store the result of that expensive operation, such that you won't need to repeat it again
To avoid a NullPointerException, a class must self-encapsulate fields that have lazy initialization. That is, a class cannot refer directly to such fields, but must access them through a method.
The hashCode method of an immutable Model Object is a common candidate for lazy initialization.
Example 1
In this example, there are two fields with lazy initialization - fHashCode and fAwards.
Example 2import java.util.*;public final class Athlete {public Athlete(int aId){//a toy implementation:fId = aId;fName = "Roger Bannister";//fAwards is not set here!}//..elided/**
Lazy initialization is used here; this assumes that awardsmay not always be of interest to the caller,and that for some reason it is particularly expensive tofetch the List of Awards.*/public List getAwards(){if ( fAwards == null ) {//the fAwards field has not yet been populated//Here is a toy implementationList<String> awards = new ArrayList<String>();awards.add( "Gold Medal 2006" );awards.add( "Bronze Medal 1998" );fAwards = awards;}return fAwards;}/**
This style applies only if the object is immutable.Another alternative is to calculate the hashCode once, when theobject is initially constructed (again, applies only when object isimmutable).*/@Override public int hashCode(){if ( fHashCode == 0 ) {fHashCode = HashCodeUtil.SEED;fHashCode = HashCodeUtil.hash(fHashCode, fId);fHashCode = HashCodeUtil.hash(fHashCode, fName);//self-encapusulated: fAwards is not referenced directly,//since it may be null:fHashCode = HashCodeUtil.hash(fHashCode, getAwards());}return fHashCode;}// PRIVATE //private int fId;private String fName;private List<String> fAwards;private int fHashCode;//Cache the hashcode inside the class}
Here, the look up of the printers available to a desktop PC is treated as an expensive operation.
Example 3 Lazy initialization is particularly useful for GUIs which take a long time to construct.import java.util.Arrays;import java.util.List;import javax.print.DocFlavor;import javax.print.PrintService;import javax.print.PrintServiceLookup;import javax.print.attribute.HashPrintRequestAttributeSet;import javax.print.attribute.PrintRequestAttributeSet;import javax.print.attribute.standard.OrientationRequested;import javax.print.attribute.standard.Sides;/** Printing services available to a desktop client. */public final class Printers {/** Print some plain text (perhaps internally converted to PDF). */void printSomething(String aText, PrintService aPrinter) {//...elided}/** Return the list of printers that can print PDFs (double-sided, portrait).*/List<PrintService> listAvailablePrinters(){if(fAvailablePrinters == null){//double-sided, portrait, for PDF files.PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();attrs.add(Sides.DUPLEX);attrs.add(OrientationRequested.PORTRAIT);//Expensive operation! This can take several seconds in some environments:fAvailablePrinters = Arrays.asList(PrintServiceLookup.lookupPrintServices(DocFlavor.INPUT_STREAM.PDF, attrs));}return fAvailablePrinters;}// PRIVATE/**
Looked up once, the first time it's needed, and then stored using astatic reference. If it was a non-static reference, thenthe list of available printers would not be looked up just once,but perhaps many times (once per 'Printers' object, and not once perloaded 'Printers' class).Self-encapsulate :If this class's implementation needs to reference this item, it must doso indirectly, by calling listAvailablePrinters().*/private static List<PrintService> fAvailablePrinters;}
There are several policies for GUI construction which a design may follow:
- always build - construct the window many times, whenever it is demanded, and do not cache the result.
- first-request build - construct the window once, when first requested. Cache the result for any further requests, should they occur.
- background build - construct the window once, in a low priority worker thread, when the system is initialized. Cache the result for any requests, should they occur.
package hirondelle.stocks.preferences;import java.awt.event.*;import javax.swing.*;import java.util.*;import java.util.logging.*;import hirondelle.stocks.util.Args;import hirondelle.stocks.util.ui.StandardEditor;import hirondelle.stocks.util.ui.UiUtil;import hirondelle.stocks.preferences.PreferencesEditor;import hirondelle.stocks.util.Util;/**
* Present dialog to allow update of user preferences.** <P>Related preferences are grouped together and placed in* a single pane of a <tt>JTabbedPane</tt>, which corresponds to an* implementation of {@link PreferencesEditor}. Values are pre-populated with* current values for preferences.**<P>Most preferences have default values. If so, a* <tt>Restore Defaults</tt> button is provided for that set of related* preferences.**<P>Preferences are not changed until the <tt>OK</tt> button is pressed.* Exception: the logging preferences take effect immediately, without the need* for hitting <tt>OK</tt>.*/public final class EditUserPreferencesAction extends AbstractAction {/**
* Constructor.** @param aFrame parent window to which this dialog is attached.* @param aPrefEditors contains implementations of {@link PreferencesEditor},* each of which is placed in a pane of a <tt>JTabbedPane</tt>.*/public EditUserPreferencesAction (JFrame aFrame, List<PreferencesEditor> aPrefEditors) {super("Preferences...", UiUtil.getEmptyIcon());Args.checkForNull(aFrame);Args.checkForNull(aPrefEditors);fFrame = aFrame;putValue(SHORT_DESCRIPTION, "Update user preferences");putValue(LONG_DESCRIPTION, "Allows user input of preferences.");putValue(MNEMONIC_KEY, new Integer(KeyEvent.VK_P) );fPrefEditors = aPrefEditors;}/** Display the user preferences dialog. */public void actionPerformed(ActionEvent event) {fLogger.info("Showing user preferences dialog.");//lazy construction: fEditor is created only once, when this action//is explicitly invokedif ( fEditor == null ) {fEditor = new Editor("Edit Preferences", fFrame);}fEditor.showDialog();}// PRIVATE //private JFrame fFrame;private java.util.List<PreferencesEditor> fPrefEditors;private static final Logger fLogger = Util.getLogger(EditUserPreferencesAction.class);/**
* Specifying this as a field allows for "lazy" creation and use of the GUI, which is* of particular importance for a preferences dialog, since they are usually heavyweight,* and have a large number of components.*/private Editor fEditor;/**
* Return GUI for editing all preferences, pre-populated with current* values.*/private JComponent getPrefEditors(){JTabbedPane content = new JTabbedPane();content.setTabPlacement(JTabbedPane.LEFT);int idx = 0;for(PreferencesEditor prefEditor: fPrefEditors) {JComponent editorGui = prefEditor.getUI();editorGui.setBorder(UiUtil.getStandardBorder());content.addTab(prefEditor.getTitle() , editorGui);content.setMnemonicAt(idx, prefEditor.getMnemonic());++idx;}return content;}/** Called only when the user hits the OK button. */private void saveSettings(){fLogger.fine("User selected OK. Updating table preferences.");for(PreferencesEditor prefEditor: fPrefEditors) {prefEditor.savePreferences();}}/**
* An example of a nested class which is nested because it is attached only* to the enclosing class, and it cannot act as superclass since multiple* inheritance of implementation is not possible.** The implementation of this nested class is kept short by calling methods* of the enclosing class.*/private final class Editor extends StandardEditor {Editor(String aTitle, JFrame aParent){super(aTitle, aParent, StandardEditor.CloseAction.HIDE);}public JComponent getEditorUI () {JPanel content = new JPanel();content.setLayout( new BoxLayout(content, BoxLayout.Y_AXIS) );content.add( getPrefEditors() );//content.setMinimumSize(new Dimension(300,300) );return content;}public void okAction() {saveSettings();dispose();}}}
No comments:
Post a Comment