//
you're reading...
Eureka!

Override becomes overload! – something to watch out with Java Generics

Recently I was working on this large project originally written in Java 1.4. In this particular effort we were refactoring parts of the project while moving the source code to Java 6 (introducing Generics) as well as using some of the new libraries available. Everything was going smooth (a little too smooth maybe), until we hit a regression defect. One of the extended processes in the application was no longer working…

As I mentioned earlier it was a large project, everything compiled fine, the unit tests worked fine and static code analysis and code review looked OK. It took us a while to figure out what went wrong: with the introduction of generics, an overridden method had become overloaded!

Funny… let me try an explain with a small example. For the purposes of this example let us consider two simple beans BaseBean and ExtensionBean:

public abstract class BaseBean {
	// Fields, methods etc.
}

public class ExtensionBean extends BaseBean {
	// Extension stuff
}

Now, let us assume we have an interface, a Processor for the ExtensionBean as well a special case processor (extending the regular processor) of ExtensionBean which is used for certain use cases:

public interface Processor {
	/**
	 * Our process method
	 */
	public void process (BaseBean bean);
}

/**
 * This is the regular processor for the extension bean
 */
public class ExtensionBeanProcessor implements Processor{

	public void process(BaseBean bean) {
		processInternal (bean, 1);
	}

	protected void processInternal (BaseBean bean, int value) {
		// Regular processing
		System.out.println("Regular Processing");
	}
}

/**
 * This is an additional special case processor for he extension bean
 */
public class SpecialExtensionBeanProcessor extends ExtensionBeanProcessor {

	protected void processInternal(BaseBean bean, int value) {
		// Do something else
		System.out.println("Extended Processing");
	}
}

If we run a simple test script:

ExtensionBean b = new ExtensionBean();
new ExtensionBeanProcessor().process(b);
new SpecialExtensionBeanProcessor().process(b);

We get the following output:

Regular Processing
Extended Processing

Assuming this was the state of the code before we started our effort, we now introduce Generics into the mix. What I have not mentioned so far is that the project had a few hundred Processors spread across and handling multiple extensions of BaseBean. Most of the processors were specialized for certain types of bean extensions and the first step most of them did was to cast the object. So, one of our targets for generics was the processor, and we changed it to:

/**
 * @param <B> The type of bean the processor handles
 */
public interface Processor <B> {
	/**
	 * Our process method - note the change in method signature now
	 */
	public void process (B bean);
}

The ExtensionBeanProcessor was changed to (note the generics and the method signature):

/**
 * This is the regular processor for the extension bean
 */
public class ExtensionBeanProcessor implements Processor<ExtensionBean> {

	@Override
	public void process(ExtensionBean bean) {
		processInternal (bean, 1);
	}

	protected void processInternal (ExtensionBean bean, int value) {
		// Regular processing
		System.out.println("Regular Processing");
	}
}

This is where things went wrong. A run of the same test as above now returns (note the second output):

Regular Processing
Regular Processing

Note that we mentioned the code was initially written in Java 1.4, so we did not have the @Override tag anywhere. The developer forgot about the overridden internal method (processInternal) in SpecialExtensionBeanProcessor, there was no compilation error. The processInternal method in SpecialExtensionBeanProcessor has now become an overloaded method rather than an overridden method.

A second set of eyes mostly looked for the interface methods process(B) on all the processors, and with a few hundred of them to review, this one particular guy was missed.

The fix: was actually pretty simple once identified, we changed the method signature of processInternal in SpecialExtensionBeanProcessor to take in a ExtensionBean and the world made sense again!

Advertisements

Discussion

No comments yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

About Random Musings

Random Musings is a collection of my personal thoughts, opinions, views and anything else that I find interesting. The views posted here are mine and may not necessarily reflect the views of MindTree Limited (my employer).
%d bloggers like this: