views:

174

answers:

2

Possible Duplicate:
Is this valid Java?

I was surprised to discover the Java class below compiles. It has several method, with the same name, number of arguments and following type-erasure types of argument. Yet, it compiles and works as expected, on windows using various versions of the Sun JDK 1.6 compiler. So if this is a bug it has been around for ages....

It has also compiled with numerous versions of Eclipse, but does not compiler with the compiler that ships with Eclipse 3.6

In addition calling code works as expected - ie. there are no errors about ambigious methods on the calling code.

If you iterate over the methods returned by ErasureExample.class.getMethods() they are all present.....

According to the JLS it would be illegal if the methods have "override-equivalent" signatures - strictly they don't, as none of Collection, Collection nor Collection are override equivalent.... if that's the case the Eclipse is wrong, JDK correct...

Feature or bug? Should it compile?

/**
 * Demonstrates that a class with methods that differ only by return type can exist.
 * Section 8.4 of the JLS suggests this is an error IFF the methods had
 * override equivalent signatures, which they dont''
 *
 * 
 * From JLS 8.4...

 * It is a compile-time error for the body of a class to declare as members two methods 
 * with override-equivalent signatures (§8.4.2) (name, number of parameters, and types
 * of any parameters). 
 *
 * Should it compile?
 */
public class ErasureExample {
   // note the single Collection<Integer> argument...
   public static int[] asArray(Collection<Integer> vals) {
      if (vals == null)
         return null;

      int idx = 0;
      int[] arr = new int[vals.size()];
      for (Integer i : vals) {
         arr[idx] = i==null? 0 : i.intValue();
         idx++;
      }
      return arr;
   }

   // same method name as above, type differs only by generics.... surely this violates 8.4 of JLS...
   public static long[] asArray(Collection<Long> vals) {
      if (vals == null)
         return null;

      int idx = 0;
      long[] arr = new long[vals.size()];
      for (Long i : vals) {
         arr[idx] = i==null? 0 : i.longValue();
         idx++;
      }
      return arr;
   }

   // same method name as above, type differs only by generics.... surely this violates 8.4 of JLS...
   public static boolean[] asArray(Collection<Boolean> vals) {
      if (vals == null)
         return null;

      int idx = 0;
      boolean[] arr = new boolean[vals.size()];
      for (Boolean b : vals) {
         arr[idx] = b==null? false : b.booleanValue();
         idx++;
      }
      return arr;
   }

}
A: 

These methods do not have the same signatures at all. Different return values and different parameter types. Yes indeed, generics do make a big difference. Everything looks good here.

JediPotPie
correction :return value is not part of the signature
schar
Actually, at the bytecode level, the method descriptors contain the return type as well, and can be used to differentiate between them. The compiler can sometimes take advantage of that, by checking the type of the variable you save the result into, since the compiler can tell what method should be called, and in the bytecode it's just the instruction 'call method with descriptor this-and-that'. It's not exactly right, according to the specifications, but the compiler permits it for now. Apparently in Java 7 they might change this.
Andrei Fierbinteanu
After playing a little with this code, I find the explanation of Andrei Fierbinteanu the most convincing. There seems to be no practical problems with compiling methods with identical signature, since the calls in the bytecode are based on unique numeric ids of the methods. Some compilers take advantage of the fact that they have generic information in order to make this possible. This causes problems. See for example what happens with reflection: ErasureExample.class.getDeclaredMethod("asArray", Collection.class) returns the first method in source code!(ie reordering the code changes things)
Eyal Schneider
They do have different signatures, asArray(Collection<Integer> vals) is not the same as asArray(Collection<Boolean> vals) because of the generics specified. True, the return type is not part of the signature but you will get a compile time error if you try to call; ArrayList<Integer> a = new ArrayList<Integer>(); boolean[] b = ErasureExample.asArray(a);That's all I was saying.
JediPotPie
+2  A: 

The compiler is smart enough to disambiguate the methods at compile-time, although in the end it performs type erasure. The whole point of parameterizing generics is to provide a compile-time type safety check, type erasure is just an implementation detail.

Gary