Variable arguments allow you to specify that a method can take multiple arguments of the same type, and don't require that the number of arguments be pre-determined (at compile- or runtime). This is one of the integral parts of Tiger, in fact, as several of the new features of the language actually incorporate varargs..
First, get used to typing the ellipsis (...). Those three little dots are the key to varargs, and you'll be typing them quite often. Here's a version of the Guitar constructor that uses varargs to allow for an indeterminate number of String features:
NOTE
All these constructors are shown, completed, in the source code for the com.oreilly.tiger.ch05.Guitar class.
public Guitar(String builder, String model, String... features);
The argument String... features indicates that any number of String arguments may be supplied. So all of the following invocations are legal:
Guitar guitar = new Guitar("Martin", "HD-28V", "Hot-rodded by Dan Lashbrook", "Fossil Ivory Nut", "Fossil Ivory Saddle", "Low-profile bridge pins"); Guitar guitar = new Guitar("Bourgeois", "OMC", "Incredible flamed maple bindings on this one."); Guitar guitar = new Guitar("Collings", "OM-42", "Once owned by Steve Kaufman--one of a kind");
You could add the same variable-length argument to the other constructors:
public Guitar(String builder, String model, GuitarWood backSidesWood, GuitarWood topWood, float nutWidth, String... features) public Guitar(String builder, String model, GuitarWood backSidesWood, GuitarWood topWood, float nutWidth, GuitarInlay fretboardInlay, GuitarInlay topInlay, String... features)
Example 5-1 shows a simple class that puts this all together, and even uses delegation to pass some varargs around.
package com.oreilly.tiger.ch05; public class Guitar { private String builder; private String model; private float nutWidth; private GuitarWood backSidesWood; private GuitarWood topWood; private GuitarInlay fretboardInlay; private GuitarInlay topInlay; private static final float DEFAULT_NUT_WIDTH = 1.6875f; public Guitar(String builder, String model, String... features) { this(builder, model, null, null, DEFAULT_NUT_WIDTH, null, null, features); } public Guitar(String builder, String model, GuitarWood backSidesWood, GuitarWood topWood, float nutWidth, String... features) { this(builder, model, backSidesWood, topWood, nutWidth, null, null, features); } public Guitar(String builder, String model, GuitarWood backSidesWood, GuitarWood topWood, float nutWidth, GuitarInlay fretboardInlay, GuitarInlay topInlay, String... features) { this.builder = builder; this.model = model; this.backSidesWood = backSidesWood; this.topWood = topWood; this.nutWidth = nutWidth; this.fretboardInlay = fretboardInlay; this.topInlay = topInlay; } }
When you specify a variable-length argument list, the Java compiler essentially reads that as "create an array of type <argument type> ". You typed:
public Guitar(String builder, String model, String... features)
NOTE
You'll get a compiler error from this, and one that's not all that descriptive of the real problem.
However, the compiler interprets this as:
public Guitar(String builder, String model, String[] features)
This means that iteration over the argument list is simple (as shown in "Iterating Over Variable-Length Argument Lists"), as is any other programming tasks you need to undertake. You can work with varargs just as you would with arrays.
However, there are some limitations. First, you can only use one ellipsis per method. Thus, the following is illegal:
public Guitar(String builder, String model, String... features, float... stringHeights)
Additionally, the ellipsis must appear as the last argument to a method.
...if you don't have any features to pass in? That's fine. Just call the constructor in the old way:
Guitar guitar = new Guitar("Martin", "D-18");
Look closely, thoughthere is no constructor with the following signature:
public Guitar(String builder, String model)
So, what gives? Well, as an added bonus to varargs, not passing in an argument is a legitimate option. So when you see String... features, you should think "zero or more String arguments." That saves you from creating another constructor without the varargs parameter.