...
In this noncompliant code example, the BadOverloading OverLoader class holds a HashMap instance and has overloaded getData() methods. One getData() method chooses the record to return on the basis of its key value in the map; the other, on the basis of the actual mapped value. For purposes of overload resolution, the signatures of the getData() 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 both 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.
| Code Block | ||
|---|---|---|
| ||
class BadOverloadingOverLoader extends HashMap<Integer,Integer> { HashMap<Integer,Integer> hm; public BadOverloadingOverLoader() { hm = new HashMap<Integer, Integer>(); hm.put(1, 111990000); hm.put(2, 222990000); hm.put(3, 333990000); // ssn records } public String getData(Integer i) { // Overloading sequence #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) { BadOverloadingOverLoader bo = new BadOverloadingOverLoader(); System.out.println(bo.getData(3)); // Get record at index '3' System.out.println(bo.getData((Integer)111990000)); // Get record containing data '111990000' } } |
For purposes of overload resolution, the signatures of the getData() methods differ only in the static type of their formal parameters. The OverLoader 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 both 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.
Although the client programmer might eventually deduce such behavior, other cases, such as with the List interface, may go unnoticed, as Joshua Bloch [Bloch 2008] describes:
...
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 BadOverloadingOverLoader class provided only the getData(Integer) method, the 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 autoboxed 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, thereby 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.
...
Naming the two related methods differently eliminates both the overloading and the confusion:.
| Code Block | ||
|---|---|---|
| ||
public Integer getDataByIndex(int i) { /* No longer overloaded */ }
public String getDataByValue(Integer i) { /* No longer overloaded */ }
|
...