Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Method and constructor overloading allows one declaration of two or more methods or constructors with the same name to be invoked, depending on the types of their argumentsbut with different parameter lists. The compiler inspects a call to an overloaded method or constructor and , using uses the declared types of the method parameters , decides to decide which method to useinvoke. In some cases, however, ambiguity confusion may arise because of the presence of relatively newer language features such as autoboxing and generics.

Furthermore, methods or constructors with the same parameter types that differ only in their declaration order are typically not flagged by Java compilers. Errors can result if when a developer does not fails to consult the documentation at every use of the method or constructor. A related pitfall is to associate different semantics with each of the overloaded methods or constructors. Defining different semantics sometimes necessitates different orderings of the same method parameters, creating a vicious circle. Consider, for example, a an overloaded getDistance() method whose where one overloading returns the distance traveled from the source while another (with rearranged parameters) returns the uncovered remaining distance to the destination. An implementer may not fail to realize the difference unless he consults the documentation is consulted at every use.

Noncompliant Code Example (Constructor)

Constructors cannot be overridden and can only be overloaded. This noncompliant code example shows the constructor Con with three overloadings: Con(int, String), Con(String, int), and Con(Integer, String).

...

Failure to exercise caution while passing arguments to these constructors can create confusion because calls to these overloadings can contain the same number of similarly typed actual parameters. Overloading must also be avoided when the overloaded constructors or methods provide inconsistent functionality for arguments formal parameters of the same types, differing just solely in their declaration order.

Compliant Solution (Constructor)

This compliant solution uses avoids overloading by declaring public static factory methods instead having distinct names in place of the public class constructors:

Code Block
bgColor#ccccff
public static Con conName1(int i, String s) { /* Initialization Sequence #1 */ }
public static Con conName2(String s, int i) { /* Initialization Sequence #2 */ }
public static Con conName3(Integer i, String s) { /* Initialization Sequence #3 */ }

Noncompliant Code Example (Method)

In this noncompliant code example, the BadOverloading class holds a HashMap instance and returns a particular record to the caller has overloaded getData() methods. One getData() method chooses the record to return on the basis of either its key value in the map or ; the other on the basis of the actual mapped value. The For purposes of overload resolution, the signatures of the getData() method is overloaded to return the contained data indexed by the key value in the former case. In the latter case, it checks whether a particular record exists before formatting and returning it as a String object. The  methods differ only in the static type of their formal parameters. The BadOverloading class inherits from java.util.HashMap and overrides its get() method to provide the checking functionality. This implementation can be extremely confusing to the client who expects the getData() methods to behave in a similar fashion and not depend on whether an index of the record or the value to be retrieved is specified.A further problem is that in the presence of autoboxing, an int argument may invoke the undesired overloading for Integer. This can happen if the overloading with the primitive int type is added to a class at a later date. Clients who expect the getData() method to fetch data on the basis of its value will suddenly start invoking the new overloading whenever an int argument is passed and proceed to return the record by index. This happens because a primitive argument becomes more specific in the new version, whereas in the old one, autoboxing allows the selection of the method with the Integer type parameter when an int is passed.

Code Block
bgColor#FFCCCC
class BadOverloading extends HashMap<Integer,Integer> {
  HashMap<Integer,Integer> hm;
  public BadOverloading() {
    hm = new HashMap<Integer, Integer>();
    hm.put(1, 111990000); hm.put(2, 222990000); hm.put(3, 333990000);  // ssn records	  
  }
  public Integer getData(int i) { // Overloading sequence #1
    return hm.get(i); // Get record at position 'i'
  }
  public String getData(Integer i) { // Overloading sequence #2#1
    String s = get(i).toString(); // Get a particular record
    return (s.substring(0, 3) + "-" + s.substring(3, 5) + "-" + s.substring(5, 9));
	  
  }
  public Integer getData(int i) { // Overloading sequence #2
    return hm.get(i); // Get record at position 'i'
  }
  @Override public Integer get(Object data) {  // Checks whether the ssn exists
    // SecurityManagerCheck()

    for (Map.Entry<Integer, Integer> entry : hm.entrySet()) {
      if(entry.getValue().equals(data)) {
        return entry.getValue();  // Exists 
      }
    }
    return null;
  }
  public static void main(String[] args) {
    BadOverloading bo = new BadOverloading();
    System.out.println(bo.getData(3)); // Get record at index '3'
    System.out.println(bo.getData((Integer)111990000)); // Get record containing data '111990000'
  }
}

...

Consequently, a client programmer may not fail to realize that the wrong element has been removed from the list.

A further problem is that in the presence of autoboxing, adding a new overloaded method definition can break previously working client code. This can happen when a new overloading with a more-specific type is added to an API whose methods used less-specific types in earlier versions. For example, if an earlier version of the BadOverloading class provided only the getData(Integer) method, client could correctly invoke this method by passing a parameter of type int; the result would be selected on the basis of its value because the int parameter would be auto-boxed to Integer. Subsequently, when the getData(int) method is added, the compiler resolves all calls whose parameter is of type int to invoke the new getData(int) method, consequently changing their semantics and potentially breaking previously-correct code. The compiler is entirely correct in such cases; the actual problem is an incompatible change to the API.

Compliant Solution (Method)

Naming the two related methods differently eliminates both the overloading and the confusion:

Code Block
bgColor#ccccff
public Integer getDataByIndex(int i) { /* No longer overloaded */ }

public String getDataByValue(Integer i) { /* No longer overloaded */ }

Applicability

Ambiguous or confusing uses of overloading can lead to unexpected results.

Bibliography

[API 2011]Interface Collection<E>
[Bloch 2008]Item 41, "Use Overloading Judiciously"

...