|
Tip One: Recursive Programming in Java Summary It's very easy to become too involved in prematurely optimizing Java code in terms of code size or speed. This process can result in code that is hard to read, and, therefore, difficult to maintain and debug. Follow this advice on programming explicitly to make sure your code's intent is clearly stated -- without compromising performance.
Background
1:public class Utils
2: {
3: static public Frame GetFrame(Component c)
4: {
5: if(c instanceof Frame || null==c)
6: return c==null ? null : (Frame)c;
7: return GetFrame(c.getParent());
8: }
9:}
We are going to look at this method from a number of perspectives, including style, efficiency, and generalization.
First, a simple description of the method: it is responsible for
finding the first To start, let's take a look at the routine line by line from the point of view of style, and to some degree from a performance point of view.
5: if (c instanceof Frame || null==c)
You may be wondering why we first check if the component "c" is an
instance of
6: return c==null ? null : (Frame)c;
This line of code above is a bit of a disaster from a style point of
view. First, it repetitious because it rechecks the validity of the
component reference. Next, it uses the ternary conditional operator, a
holdover from C/C++, which many people consider more difficult to read
than using a standard Finally,
7: return GetFrame(c.getParent());
Generally, when a method calls itself, this is termed
recursion. This particular form of recursion -- where the
method calls itself as the very last expression before the return
statement -- is called tail recursion since the method is
"chasing its own tail" so to speak. This use of tail recursion is
basically just an expression of code reuse. In other words, the routine
is using recursion as a simple and "cheap" way of performing looping --
each recursive invocation of
Addressing the problem
First off, let's replace the recursion with an explicit
static public Frame GetFrame(Component component)
{
Frame resultingFrame = null;
while (component != null && resultingFrame == null)
{
if (component instanceof Frame) resultingFrame = (Frame)component;
else component = component.getParent();
}
return resultingFrame;
}
Okay, for you byte counters out there, this version does result in
three additional bytecode instructions than the original,
recursive version has -- 19 versus 16. However, the new, explicit form
never has to worry about running out of stack space -- the routine is
using a single stack variable, The greatest benefit of this new, explicit version is the increased readability, simplicity, and maintainability of the code. So, we have created a version that is safer (no call stack limitations), faster (explicit looping rather than recursion), and clearer about what it's really doing.
Taking the routine a step further There are many ways to make this routine more generic. We can make it generic along a number of dimensions including the containment hierarchy and the target parent specification. In this particular case, we are going to limit this routine to sticking to the component container hierarchy but allow the caller to specify the target parent class.
First, here is an example of how you would use the new method to
determine a
Applet dadApplet = (Applet)GetParent(myButton,"Applet");
In this new version we replace the use of the
static public Component GetParent(Component component, String compType )
{
Component resultingComponent = null;
while (component != null && resultingComponent == null)
{
if ( compType.equals( component.getClass().getName() ) )
resultingComponent = component;
else component = component.getParent();
}
return resultingComponent;
}
In terms of bytecode instructions, we have introduced one more invoking
bytecode because
Conclusion |