JDK-4313888 : Concise array literals
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.io
  • Affected Version: 1.4.0
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: generic,solaris_8
  • CPU: generic
  • Submitted: 2000-02-18
  • Updated: 2003-09-05
  • Resolved: 2003-09-05
Related Reports
Relates :  
Relates :  
Description

Name: mr33420			Date: 02/18/2000


The Java programming language provides no convenient way to pass a variable
number of arguments to a method, similar to the varargs feature of C or the
&rest keyword of Common Lisp.  This feature is extremely useful in GUI APIs, in
constructing and manipulating collections, and in APIs for formatted output.

One of the goals of the New I/O project, for example, is to provide a simple
API for formatted textual output similar to the well-known printf procedure of
the standard C library.  Developers have been asking for this feature for
years; adding it will make Java that much more attractive an alternative to C,
C++, and Visual Basic.

The central method of such an API accepts a format string followed by some
arguments.  The exact number of arguments required depends upon the content of
the format string.  Ideally we'd like to be able to write something like:

    Formatter f = new Formatter(System.out);
    f.fmt("%d bytes in %d seconds (%.2f KB/s)\n",
          nbytes, seconds, ((double)(nbytes / 1024) / (double)seconds));

The Java programming language, as presently defined, does not allow this.

This feature is being developed as JSR-065.

Comments
WORK AROUND Name: mr33420 Date: 02/18/2000 There are three ways to support varying numbers of arguments without changing the language. (1) Massive overloading: Declare many different methods, all with the same name but different signatures, reflecting all the possible combinations of argument counts and types. In the Formatter API, for example, declare public class Formatter { public void fmt(String fmt, int arg1) { ... } public void fmt(String fmt, String arg1) { ... } public void fmt(String fmt, int arg1, int arg2) { ... } public void fmt(String fmt, int arg1, String arg2) { ... } public void fmt(String fmt, String arg1, int arg2) { ... } public void fmt(String fmt, String arg1, String arg2) { ... } public void fmt(String fmt, int arg1, int arg2, int arg3) { ... } ... } Each method would simply package up its arguments and pass control to a common internal formatting method. This technique can work if there are only a few possible types and if there is a fairly small lower bound on the total number of arguments. Otherwise the number of methods required tends to explode combinatorically. For the formatting API there are nine argument types and no limit to the number of arguments. Even a fixed limit, say six, would require 531,441 distinct methods. (2) Object arrays: Declare a single formatting method that takes a format string and an array of objects, requiring the invoking code to package its arguments into an object array itself: public class Formatter { ... public void fmt(String format, Object[] args) { ... } ... } f.fmt("%d bytes in %d seconds (%.2f KB/s)\n", new Object[] { new Integer(nbytes), new Integer(seconds), new Double((double)(nbytes / 1024) / (double)seconds)}); This technique, which is used in several existing Java platform APIs, works but is very tedious to use. In this example it has bloated two lines of fairly readable code into six lines that intermix the code for constructing the object array with the code for computing the argument values. (3) Chained methods: Declare a single formatting method that takes a format string and returns an auxiliary "argument collector" object that collects and processes the arguments. public class Formatter { ... public ArgumentCollector fmt(String format) { ... } ... } The ArgumentCollector class has one method for each possible argument type as well as an "end" method to identify the end of the argument list. The argument methods return the ArgumentCollector object itself, allowing arguments to be passed with a fairly compact syntax: f.fmt("%d bytes in %d seconds (%.2f KB/s)\n") .a(nbytes).a(seconds).a((double)(nbytes / 1024) / (double)seconds) .end(); The chained-method technique supports much more concise use cases than object arrays. It is, unfortunately, complicated by the need for an auxiliary class to handle argument collection. It is also a bit more error-prone: If the user forgets to invoke the "end" method then no output will appear. This is a fairly annoying type of bug to find and fix. ======================================================================
11-06-2004

SUGGESTED FIX Name: mr33420 Date: 02/18/2000 None of the described workarounds is completely satisfactory. The first is impractical in general, the second is extremely verbose, and the third is rather odd in appearance and somewhat error prone. These choices suggest that a very small, tightly focussed change to the Java programming language might be a better solution. After discussing several alternatives with gbracha@eng (maintainer of the Java Language Specification), maddox@eng (maintainer of the Java compiler), members of the TRC, and various other interested parties, the best approach appears to be to add a facility for concise, automatically-boxing object-array literals. In short, the syntax of expressions would be extended to allow a list of expressions within curly braces, e.g., int i; double x; String s; ... { i, x, s } ... This would be expanded by the compiler into an object-array literal that "boxes" any values of primitive type using the appropriate wrapper classes from the java.lang package: ... new Object[] { new Integer(i), new Double(x), s } ... Thus the above formatting example could be written very concisely as: f.fmt("%d bytes in %d seconds (%.2f KB/s)\n", { nbytes, seconds, ((double)(nbytes / 1024) / (double)seconds) }); This proposal has the following advantages: - The use cases are nearly as compact as the ideal syntax shown above. Only two more tokens (the curly braces) are required. - The declaration of methods that accept varying numbers of arguments is no different than in the object-array case. This makes it easy to define varargs-style methods that exploit this feature. - This new syntax can be used with existing APIs that accept object arrays without having to change those APIs. The Swing API, e.g., has numerous constructors and methods that take object arrays and are prime candidates for the new syntax (e.g, javax.swing.table.DefaultTableModel). Similar cases can be found in the collections framework (e.g., Arrays.asList). - This is a very narrow language change, being essentially an abbreviation of the more general array-initializer syntax for an extremely common case. Extending the bytecode compiler to support the new syntax should be very straightforward and low risk. - This proposal does a better job of preserving "linguistic transparency" than the ideal syntax. The latter syntax boxes primitive values and creates an object array without providing any visual cue that such work is being done. The proposed syntax uses the curly-brace delimiters to indicate that something special is going on. The only apparent disadvantage to this proposal is that a minor language change is required. CHANGES TO THE JLS (contributed by gbracha@eng) Below is JLS language for a more concise form of array literals with built-in boxing. In this proposal, {x1, ..., xn} for n >= 0 is an array literal constructor. If xi is a value of primitive type, it is boxed. The type of this literal is either Object[] (simple version) or inferred a la GJ (TBD). JLS sections affected: Expressions: Add a new kind of literal. JLS 15.8.3 Reference Array Literals ReferenceArrayLiteral -> { Expr {, Expr} } Reference array literals define literal array values, where the elements of the array are all of reference type. If any subexpression of a reference array literal is not of a reference type, it is subjected to boxing conversion (5.1.7). Evaluation of a reference array literal with n immediate subexpressions first produces an instance of class Object[] of length n. Evaluation of a reference array literal then proceeds from left to right. The kth immediate subexpression is evaluated. The value that results is then subject to boxing conversion (5.1.7), and the result is placed into the array created earlier, at index k-1. If the evaluation of any immediate subexpression completes abruptly, the entire reference array literal expression completes abruptly, for the same reason. The type of a reference array literal is Object[] (alternately: is defined by the type following type inference algorithm ...). Conversions: Define boxing conversions (JLS 5.1.7 and/or 5.5?) Boxing conversion converts values of primitive type to corresponding values of reference type as follows: If i is a value of type boolean, then boxing conversions converts i into a reference a reference r of class Boolean, such that r.value() == i. If i is a value of type byte, then boxing conversions converts i into a reference a reference r of class Byte, such that r.value() == i. If i is a value of type char, then boxing conversions converts i into a reference a reference r of class Character, such that r.value() == i. If i is a value of type short, then boxing conversions converts i into a reference a reference r of class Short, such that r.value() == i. If i is a value of type int, then boxing conversions converts i into a reference a reference r of class Integer, such that r.value() == i. If i is a value of type long, then boxing conversions converts i into a reference a reference r of class Long, such that r.value() == i. If i is a value of type float, then boxing conversions converts i into a reference a reference r of class Float, such that r.value() == i. If i is a value of type double, then boxing conversions converts i into a reference a reference r of class Double, such that r.value() == i. If i is a value of any other type, boxing conversion is equivalent to an identity conversion (5.1.1). Note that this formulation disallows any assumptions about the identity of the boxed values on the programmer's part. This would allow (but not require) sharing of some or all of these references (canonical booleans, for example). Other changes Corrections to 5.1.5 Forbidden conversions: Some forbidden conversions are no longer forbidden. Cross references to reference array literals in JLS 10 (arrays chapter). Syntax (grammar modifications for parsing). ======================================================================
11-06-2004

PUBLIC COMMENTS ..
10-06-2004

EVALUATION Superseded by varargs in Tiger. -- ###@###.### 2003/9/5
09-11-0171