From Effective Java 2/e by Joshua Bloch
- The choice of which overloading to invoke is made at compile time
- Selection among overloaded methods is static, while selection among overridden methods is dynamic.
- The compile-time type of an object has no effect on which method is executed when an overridden method is invoked; the “most specific” overriding method always gets executed. Compare this to overloading, where the runtime type of an object has no effect on which overloading is executed;
Example shows difference between overloading and overriding
// It prints Unknown Collection three times public class CollectionClassifier { public static String classify(Set<?> s) { return "Set"; } public static String classify(List<?> lst) { return "List"; } public static String classify(Collection<?> c) { return "Unknown Collection"; } public static void main(String[] args) { Collection<?>[] collections = { new HashSet<String>(), new ArrayList<BigInteger>(), new HashMap<String, String>().values() }; for (Collection<?> c : collections) System.out.println(classify(c)); } }
// It prints out wine, sparkling wine, and champagne class Wine { String name() { return "wine"; } } class SparklingWine extends Wine { @Override String name() { return "sparkling wine"; } } class Champagne extends SparklingWine { @Override String name() { return "champagne"; } } public class Overriding { public static void main(String[] args) { Wine[] wines = { new Wine(), new SparklingWine(), new Champagne() }; for (Wine wine : wines) System.out.println(wine.name()); } }
Avoid confusing uses of overloading
- A safe, conservative policy is never to export two overloadings with the same number of parameters
// ObjectOutputStream void writeBoolean(boolean); void writeInt(int); void writeLong(long); boolean readBoolean(); int readInt(); long readLong();
- For constructors, you do have the option of exporting static factories instead of constructor
- When at least one corresponding formal parameter in each pair of overloadings has a “radically different” type in the two overloadings
Confusion by auto-boxing
// It prints out [-2, 0, 2] public class ListOverloading { public static void main(String[] args) { List<Integer> list = new ArrayList<Integer>(); for (int i = -3; i < 3; i++) list.add(i); for (int i = 0; i < 3; i++) list.remove(i); // should be list.remove((Integer) i); System.out.println(list); } } public class List { void remove(int); void remove(E); }
There may be times when you feel the need to violate the guidelines in this item, especially when evolving existing classes.
- The standard way to ensure this behavior is to have the more specific overloading forward to the more general
public boolean contentEquals(StringBuffer sb) { return contentEquals((CharSequence) sb); }