Welcome!

From the Editor of Eclipse Developer's Journal

Bill Dudney

Subscribe to Bill Dudney: eMailAlertsEmail Alerts
Get Bill Dudney via: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Related Topics: Eclipse Platform

Eclipse Platform: Article

Eclipse Special: Bill Dudney Looks at the Change Method Signature Refactoring

Eclipse Special: Bill Dudney Looks at the Change Method Signature Refactoring

This column contains an excerpt from one of the refactoring chapters in my book Eclipse 3 Live. The book will eventually contain similar sections for all the refactorings available in Eclipse.

Change Method Signature

This refactoring allows you to change the signature of an existing method by changing any of the aspects of the method that make its signature. You can change the access rights (public, private, etc.), you can change the name as well as add or remove parameters, and you can add or remove exceptions that are thrown from the method.

The most typical use of this refactoring is to add or remove parameters from a method. Over time, you might discover that a method would be better designed or easier to understand or implement if it took an additional parameter. Another reason to use this refactoring is that during the process of refining/refactoring your code, you might discover that you no longer need one of the parameters that is being passed in. In addition to adding and removing parameters, this refactoring is also helpful in changing the access privileges on methods. I most commonly change access rights to be more restrictive (i.e., public to protected or private). Sometimes for retrofitting unit tests onto existing code, however, I change privileges to be less restrictive. This refactoring will change all the subclass implementations of the method to match the new access privilege.

Change Method Signature - AntiPattern Example

During some recent development on the MyFaces project, I implemented a method and then realized that I needed an additional parameter in order to make the method work properly. Specifically, I added some code to handle some of the factory lookup functionality. At first, I was only interested in getting the algorithm correct and not on how to make sure the actual configuration was set properly. The initial method is shown in Listing 1 below.

Listing 7.2 Initial performMetaInfFactoryConfig


protected FactoryConfig performMetaInfFactoryConfig(
      ExternalContext context) throws FacesException {
    Set factoryNames = FactoryFinder.getFactoryNames();
    // keyed on resource names, factory name is the value
    Map resourceNames = expandFactoryNames(factoryNames);
    //Search for factory files in the jar file
    Set services = context
        .getResourcePaths(META_INF_SERVICES_LOCATION);
    // retainAll performs the intersection of the factory
    // names that we are looking for the ones found, only the
    // services found that match the expected factory names 
    // will be retained
    services.retainAll(resourceNames.keySet());
    Iterator itr = services.iterator();
    FactoryConfig config = new FactoryConfig();
    while (itr.hasNext()) {
      String resourceName = (String) itr.next();
      InputStream is = context
          .getResourceAsStream(resourceName);
      InputStreamReader isr = new InputStreamReader(is);
      BufferedReader br = new BufferedReader(isr);
      String className = null;
      try {
        className = br.readLine();
      } catch (IOException e) {
        throw new FacesException(
            "Unable to read class name from file "
                + resourceName, e);
      }
      try {
        Class.forName(className);
      } catch (ClassNotFoundException e) {
        throw new FacesException("Unable to find class "
            + className, e);
      }
      config.setFactory((String) resourceNames
          .get(resourceName), className);
    }
    return config;
  }

So after completing this code and testing it, I realized that the configuration was not actually being updated. Following the standard of the other code in this class that did similar things, I decided to pass in the configuration object that maintained the overall configuration. And instead of just making the change and then tracking down the compilation problems, I applied the refactoring.

Change Method Signature - Apply the Refactoring

The first step in using any of the refactorings is to invoke it via the context menu in the Java editor. For this refactoring to work, you must select a method signature or any line in the method body. Keep in mind that for a particular refactoring to be available, you must have an element selected that the desired refactoring can effect. Figure 1 shows the refactoring menu item selected over the selected method name.

I almost always end up double-clicking on the method name just to make sure that I'm invoking the refactoring on the correct element.

After invoking the refactoring, you will see a dialog box that looks like Figure 2.

There are two things that need to be changed to have this method match the rest of the methods in the class. First, the return type must be changed to void and an additional parameter of type FacesConfig must be added. Since this method returns a value and the return type is being changed to void, Eclipse will complain that the result of the refactoring will not compile. You can safely hit Continue and then remove the return statement after the refactoring is complete. Figure 3 shows the updated values.

Notice that the default value for the new parameter must be specified and that it is defaulted to null. For simple values like String or int, you can specify the value in this dialog box. For more complex values, you will have to do some work after refactoring. Now that we have the values changed and specified, click the OK button. After doing some internal checking, Eclipse will report that a void method should not return a value. As stated earlier, it is OK to ignore this warning and click the Continue button. After the refactoring completes, the return statement can be deleted. There is one more small change to make; we need to apply the factory configuration to the overall configuration object facesConfig. The refactored code is shown in Listing 3

Listing 3 Refactored performMetaInfFactoryConfig


protected void performMetaInfFactoryConfig(
      ExternalContext context, FacesConfig facesConfig)
      throws FacesException {
    Set factoryNames = FactoryFinder.getFactoryNames();
    // keyed on resource names, factory name is the value
    Map resourceNames = expandFactoryNames(factoryNames);
    //Search for factory files in the jar file
    Set services = context
        .getResourcePaths(META_INF_SERVICES_LOCATION);
    // retainAll performs the intersection of the factory
    // names that we are looking for the ones found, only the
    // services found that match the expected factory names
    // will be retained
    services.retainAll(resourceNames.keySet());
    Iterator itr = services.iterator();
    FactoryConfig config = new FactoryConfig();
    while (itr.hasNext()) {
      String resourceName = (String) itr.next();
      InputStream is = context
          .getResourceAsStream(resourceName);
      InputStreamReader isr = new InputStreamReader(is);
      BufferedReader br = new BufferedReader(isr);
      String className = null;
      try {
        className = br.readLine();
      } catch (IOException e) {
        throw new FacesException(
            "Unable to read class name from file "
                + resourceName, e);
      }
      try {
        Class.forName(className);
      } catch (ClassNotFoundException e) {
        throw new FacesException("Unable to find class "
            + className, e);
      }
      config.setFactory((String) resourceNames
          .get(resourceName), className);
    }
    facesConfig.setFactoryConfig(config);
  }

Now that the code is refactored you can easily search for all the uses of the method and change the default null value to be a more appropriate value. You can use the context menu's References->Workspace to have Eclipse search the whole workspace for calls to the method. In this example the method is protected so it makes things a bit easier to do manually but it is a great excuse to get some experience using Eclipse's great search features.

More Stories By Bill Dudney

Bill Dudney is Editor-in-Chief of Eclipse Developer's Journal and serves too as JDJ's Eclipse editor. He is a Practice Leader with Virtuas Solutions and has been doing Java development since late 1996 after he downloaded his first copy of the JDK. Prior to Virtuas, Bill worked for InLine Software on the UML bridge that tied UML Models in Rational Rose and later XMI to the InLine suite of tools. Prior to getting hooked on Java he built software on NeXTStep (precursor to Apple's OSX). He has roughly 15 years of distributed software development experience starting at NASA building software to manage the mass properties of the Space Shuttle.

Comments (1) View Comments

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


Most Recent Comments
digitwiz101 06/22/04 10:37:59 AM EDT

A useful trick with this refactoring is to change the default to something like DOES_NOT_COMPILE or ZZZZZZ. This way, when you recompile after the refactoring, the compiler will immediately alert you with problems for each place where you need to examine the calls to the refactored method.