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 implementation
List<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 invoked
if ( 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