I'm interested in JComponent's get/setPreferredSize mechanism and allocation behavior
Note: I'm not interested in: how the default layout managers work. Let's think for a moment I'm using preferredSize values for an X purpose not related to default layout managers.
The official documentation fails to give a solution, so let's look at the JRE source code:
This one calls the super...
//JComponent.java (1642 ~ 1663)
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
Dimension size = null;
if (ui != null) {
size = ui.getPreferredSize(this);
}
return (size != null) ? size : super.getPreferredSize();
}
This one also calls the super...
//JComponent.java (1628 ~ 1639)
public void setPreferredSize(Dimension preferredSize) {
super.setPreferredSize(preferredSize);
}
JComponent extends Container, let's take a look:
//Container.java (1751 ~ 1800)
public Dimension getPreferredSize() {
return preferredSize();
}
@Deprecated
public Dimension preferredSize() {
Dimension dim = prefSize;
if (dim == null || !(isPreferredSizeSet() || isValid())) {
synchronized (getTreeLock()) {
prefSize = (layoutMgr != null) ?
layoutMgr.preferredLayoutSize(this) :
super.preferredSize();
dim = prefSize;
}
}
if (dim != null){
return new Dimension(dim);
}
else{
return dim;
}
}
as I thought, it's returning a new instance. But let's see what happens with the setter, which is in Component.java (since Container extends Component but doesn't override the method):
//Component.java (2560 ~ 2585)
public void setPreferredSize(Dimension preferredSize) {
Dimension old;
if (prefSizeSet) {
old = this.prefSize;
}
else {
old = null;
}
this.prefSize = preferredSize;
prefSizeSet = (preferredSize != null);
firePropertyChange("preferredSize", old, preferredSize);
}
This is just referencing to the object I passed!, perfect! (dangerous indeed, but it suits my current needs!)
I conclude I'm setting the actual object instance that my components hold and use, so (at least as for this JRE release) there is no need to repeatedly call setPreferredSize with the same object again (but failing to do so would indeed be dangerous as this source code can change in different implementations), and even so setPreferredSize won't generate garbage if we call it with the same instance of Dimension. Calling getPreferredSize, on the other hand, clearly will allocate a new Dimension object to return the value.
The answer is: keep track of your preferred sizes somewhere to minimize the use of getPreferredSize, and to be polite to the API, call setPreferredSize, but using the same Dimension instance for avoiding garbage.
Dimension pref = null;
void getNewPrefOnlyWhenNeeded() {
pref = component.getPreferredSize();
}
void calledEveryMillisecond() {
//Here or on the Constructor, MouseListener or wherever
if (pref == null || someCondition) getNewPrefOnlyWhenNeeded();
// do stuff with pref...
...
//setPreferredSize can actually be skipped if we call our layout manager after all is resized. It would be dirty tho...
component.setPreferredSize(pref);
}
After further analysis, Maximum and Minimum size methods share this mechanics, so everything solved.