Architecture & DesignHow to Write Methods Efficiently in Java

How to Write Methods Efficiently in Java

There are a few guidelines that one must adhere to while writing code that really is clean and understandable. One such simple technique is the single responsibility principle. This article glimpses over six key concepts of writing efficient methods in Java.

Overview

Methods, in a way, are the action points in Java. A method typically consists of a set of well-defined program statements. It has a name and a set of different types of arguments (0 or more). The type of processing it does is conventionally conveyed by the name it is referred to. It can be invoked at any point in the program by using its name and arguments only. It literally is a subprogram within the main program. There can be many methods in a program. Each method is distinct due to its name, number, and type of argument it takes. In an Object-Oriented Language such as Java, a method can be extended by the technique called method overloading or polymorphism. A method may or may not return a value as a final statement. The names of the method and the argument are distinctively called its signature. It is a part of method declaration.

1. Method Signature

Although a method may have different return types, it does not add to its distinctiveness. Therefore, it is not included in the ‘method signature’.

// This code will not work

public class MyClass {

   int method(){
      return10;
   }

   String method(){
      return "Hello";
   }

   public static void method1(){
   }
   public static int method1(){
   }
}

Listing 1: Method with distinct return type does not add to the distinctive need of the method

Note that the methods declared above are only distinct by their return type, which the compiler does not recognize; hence, it flags an error of a repeated declaration. Instead, if we change the arguments in the methods, they all become valid. This type of method with the same name but a different argument list is called method overloading. Note that the Java compiler distinguishes them by their signatures.

public class MyClass {
   int method(){
      return 10;
   }
   String method(int i) {
      return "Hello";
   }
   public static void method1(String a, int i){
   }
   public static int method1(float f){
      return 20.55;
   }
   public static int main (int a, int b){
     return a+b;
   }
   public static void main(String[] args){
      System.out.println(main(10,20));
   }
}

Listing 2: Valid overloaded method

The reason for not including the return type in the method signature is that, as we can see in Listing 1, there is no way to distinguish between which method is invoked from the following two valid method calls.

int ret = method();
method();

It is all right to invoke a method and ignore the result. Therefore, to prohibit ambiguity, the Java compiler does not allow method overloading on the basis of return type. A method signature is a mark of its distinctiveness, so a return type it is not taken into account as its signature.

2. Method Body

A method exists as a definition of an action. This action is explicated through a logical set of program statements. The simplest guideline to express the logic is that it must perform a single, well-defined task in as optimum a manner as possible. The definition should be simple enough to be identified the method name given. This is the single responsibility principle. The method should be reasonably short, crisp, and readable. Apply appropriate comments to enhance the readability as needed. It is better to lay the exit point logic in such a manner that there is only one return statement. For example, a code statement such as,

public static Boolean isEven(int a){
   if(a%2==0)
      return true;
   else
      return false;
}

can be easily converted as follows:

public static Boolean isEven(int a){
   Boolean flag=false;
   if(a%2==0)
      flag=true;
   return flag;
}

A shorter version may be written as follows, but it limits readability. Therefore, it should be avoided.

public static Boolean isEven(int a){
   return a%2==0;
}

This is a bit better.

public static Boolean isEven(int a){
   return a%2==0? true: false;
}

3. Method Overloading

Method overloading is when we use more than one method with the same name but different argument type, number of arguments, and combination. The Java compiler picks the appropriate method on invocation according to the signature. For example, we can find a number of methods overloaded in the String class of Java API, such as:

int indexOf(int ch);

Returns the index within this string of the first occurrence of the specified character. (Java API Doc)

int indexOf(String str, int fromIndex);

Returns the index within this string of the first occurrence of the specified character, starting the search at the specified index. (Java API Doc)

int indexOf(String str);

Returns the index within this string of the first occurrence of the specified substring. (Java API Doc)

Method overloading may remind you of generics in Java but note that they are not the same. They serve different purposes. We’ll pick them up in separate article because they need more explanation. Nevertheless, they can go side by side and are a pretty powerful combination.

4. Method Overriding

Method overriding is where a method is inherited from a parent class by the child class to extend its implementation to convey a new meaning. It is significantly different from method overloading in the sense that here the method name, type, and number of arguments must remain the same all throughout the inheritance. We typically use the @Override annotation to emphasize that we intend to override the parent method and not overload it.

public class BasicEmployee {
   float basic;
   public float updateSalary(Number val){
      return basic+val.floatValue();
   }
}
public class BasicPlusCommissionEmployee extends
      BasicEmployee {
   @Override
   public float updateSalary(Number commission){
      return basic+basic*commission.floatValue();
   }
}

The Java compiler flags an error if we, by any chance, make any mistake in the method signature. This is the reason the @Override annotation is very important.

5. Recursion

A method that calls itself to perform a calculation is called a recursive method. Recursive logic often makes the implementation logic terse and crisp. There is no such recursive logic that cannot be implemented with simple loops. But, the main use of recursive implementation is to make the program logic short and simple. There is some specific calculation which can be better defined through recursive logic, such as implementing Tower of Hanoi, GCD, factorial, and so forth. It is better to avoid unnecessary recursion because it may obfuscate code to the verge of making it hard to debug and read. Recursion heavily uses a system stack and, based upon the stack size, the JVM may throw a StackOverflowError exception at runtime. It is unlikely, yet a probable situation depending upon how deep the call chain goes.

6. Method Documentation

Although Java never enforces one, methods should be documented by using the Javadoc tool. This is particularly useful if we are developing some sort of a library or a framework. A well-documented code helps other programmers to understand the purpose of the method, number, and type of argument it takes and the constraints associated with the implementation. It also gives an idea about the exception it may raise and the value it returns, if any.

   /**
    * Translates a byte array containing the two's-complement
    * binary representation of a BigInteger into a BigInteger.
    * The input array is assumed to be in <i>big-endian</i>
    * byte-order: the most significant byte is in the zeroth
    * element.
    *
    * @param  val big-endian two's-complement binary representation
    *         of BigInteger.
    * @throws NumberFormatException {@code val} is zero bytes long.
    */
   public BigInteger(byte[] val) {
      if (val.length == 0)
         throw new NumberFormatException("Zero length BigInteger");
      if (val[0] < 0) {
         mag = makePositive(val);
         signum = -1;
      } else {
         mag = stripLeadingZeroBytes(val);
         signum = (mag.length == 0 ? 0 : 1);
      }
      if (mag.length >= MAX_MAG_LENGTH) {
         checkRange();
      }
   }

The preceding code is an extract from the Java API source to illustrate verbosity. Popular Java IDEs, like Eclipse and IntelliJ Idea, pick up the documentation to provide useful information while coding. This immensely helps programmers at all levels to get a precise idea about the purpose and syntax of the method quickly.

Conclusion

These six vital but not comprehensive points should be adhered to in writing efficient methods in Java. A code that does not enhance readability is not a good code, however cleverly it may be implemented. Remember that efficiency lies not in employing tricky code but on simplicity. It is good idea to divide a complicated logic into more than make one big method. Try debugging such code you’ll understand what it takes. The key idea is to apply the KISS principle as far as possible.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories