Wednesday, December 27, 2006

Another kludge in Java - varargs

Varargs in Java. Seriously, who needed this?

You're probably aware of my position on Java Generics, how I view their implementation to be a horrific kludge, a compromise made in name of backward compatibility where the backward compatibility issues are in fact nonexistent.

Now, here's a new discovery - vararg methods. Someone asked on the Rhino list whether we can support them, and I answered that as long as he passes the vararg argument explicitly as an array, then yes. I decided to investigate the problem further within a codebase I'm truly familiar with - FreeMarker's BeansWrapper. I quite quickly came up with a scheme for supporting non-overloaded vararg methods. However, for overloaded ones, the exercise becomes, shall we say, a bit more involved. As I had to discover, the overloaded method resolution algorithm from Java Language Specification (JLS) 2nd edition was seriously overwritten into a three-stage algorithm in JLS 3rd edition, of which the "old" (mathematically quite elegant) algorithm from JLS 2 became the second stage. I probably need some additional time to grasp it, but it looks quite horrible now. If you wish, compare:

"Method Invocation Expressions" section in JLS 2 

with

"Method Invocation Expressions" section in JLS 3.

Of course, the chapters regarding type conversions (required, as overloaded method resolution relies on the definition of "method invocation conversions", a subset of conversions that is allowed between actual and declared parameter types when invoking a method) are also substantially rewritten to cope with boxing, unboxing, and generic types.

And the worst part? Varargs are yet another Java the Language feature implemented without a Java the Virtual Machine support. They're purely syntactic sugar, with vararg argument being an array on bytecode level. Several annoying things:

  1. reflection is unaware of it. You can't just pass more arguments to the method, you have to pass the vararags arguments in an explicitly allocated array.
  2. No automatic object-to-primitive conversion when using reflection. If the vararg method's type is "int...", you must pass int[], not an Integer[]. Compare this with automatic 
    conversion for regular argument types.
  3. Unintuitive behaviour. Quick, should the following example print 1 or 0:
public class Test {
  static void x(Object... i) {
    System.out.println(i.length);
  }
  public static void main(String[] args) {
    x(new Object[0]);
  }
}

No comments: