Varargs: аргументы переменной длины

В JDK 5 была добавлена новая функциональная возможность, которая упрощает создание методов, принимающих переменное количество аргументов. Эта функциональная возможность получила название varargs (сокращение термина variable-length arguments — аргументы переменной длины). Метод, который принимает переменное число аргументов, называют методом переменной арности, или просто методом varargs.

Ситуации, в которых методу нужно передавать переменное количество аргументов, встречаются не так уж редко. Например, метод, который открывает подключение к Internet, может принимать имя пользователя, пароль, имя файла, протокол и тому подобное, но применять значения, заданные по умолчанию, если какие-либо из этих сведений опущены. В этой ситуации было бы удобно передавать только те аргументы, для которых заданные по умолчанию значения не применимы. Еще один пример — метод printf (), входящий в состав библиотеки ввода-вывода Java. Как будет показано в главе 19, он принимает переменное число аргументов, которые форматирует, а затем выводит.

До появления версии J2SE 5 обработка аргументов переменной длины могла выполняться двумя способами, ни один из которых не был особенно удобным. Во-первых, если максимальное количество аргументов было небольшим и известным, можно было создавать перегруженные версии метода — по одной для каждого возможного способа вызова метода. Хотя этот способ подходит для ряда случаев, он применим только к узкому классу ситуаций.

В тех случаях, когда максимальное число возможных аргументов было большим или неизвестным, применялся второй подход, при котором аргументы помещались в массив, а затем массив передавался методу. Следующая программа иллюстрирует этот подход.

// Использование массива для передачи методу переменного
// количества аргументов. Это старый стиль подхода
// к обработке аргументов переменной длины.
class PassArray {
static void vaTest(int v[]) {
System.out.print("Количество аргументов: " + v.length + " Содержимое: ");
for (int x : v)
System.out.print(x + " ") ;
System.out.println ();
}
public static void main(String args[]) {
// Обратите внимание на способ создания массива
// для хранения аргументов.
int nl[] = { 10 };
int п2[] = { 1, 2, 3 };
int пЗ []={};
vaTest(nl); // 1 аргумент
vaTest(n2); // 3 аргумента
vaTest(nЗ); // без аргументов

Эта программа создает следующий вывод:

Количество аргументов: 1
Содержимое: 10
Количество аргументов: 3
Содержимое: 1 2 3
Количество аргументов: 0
Содержимое:

В программе методу vaTest () аргументы передаются через массив v. Этот старый подход к обработке аргументов переменной длины позволяет методу vaTest () принимать любое число аргументов. Однако он требует, чтобы эти аргументы были вручную помещены в массив до вызова метода vaTest (). Создание массива при каждом вызове метода vaTest () не только трудоемкая, но и чреватая ошибками задача. Функциональная возможность использования методов varargs обеспечивает более простой и эффективный подход.

Для указания аргумента переменной длины используют три точки (...). Например, вот как метод vaTest () можно записать с использованием аргумента Переменной длины:

static void vaTest(int ... v) {

Эта синтаксическая конструкция указывает компилятору, что метод vaTest () может вызываться с нулем или более аргументов. В результате v неявно объявляется как массив типа int []. Таким образом, внутри метода vaTest () доступ к v осуществляется с использованием синтаксиса обычного массива. Предыдущая программа с применением метода vararg приобретает следующий вид:

// Демонстрация использования аргументов переменной длины.
class VarArgs {
// теперь vaTest () использует аргументы переменной длины
static void vaTest(int ... v) {
System.out.print("Количество аргументов: " + v.length + " Содержимое: ");
For(int x : v)
System.out.print (x + " ");
System.out.println ();
}
public static void main(String args[]) {
// Обратите внимание на возможные способы вызова
// vaTest () с переменным числом аргументов.
vaTest (10); //1 аргумент
vaTest (1, 2, 3); //3 аргумента
vaTest (); // без аргументов
}
}

Вывод этой программы совпадает с выводом исходной версии.

Отметим две важные особенности этой программы. Во-первых, как уже было сказано, внутри метода vaTest () переменная v действует как массив. Это обусловлено тем, что v является массивом. Синтаксическая конструкция ... просто указывает компилятору, что метод будет использовать переменное количество аргументов, и что эти аргументы будут храниться в массиве, на который ссылается переменная v. Во-вторых, в методе main () метод vaTest () вызывается с различным количеством аргументов, в том числе, и вовсе без аргументов. Аргументы автоматически помещаются в массив и передаются переменной v. В случае отсутствия аргументов длина массива равна нулю.

Наряду с параметром переменной длины массив может содержать "нормальные" параметры. Однако параметр переменной длины должен быть последним параметром, объявленным методом. Например, следующее объявление метода вполне допустимо:

int dolt(int a, int b, double с, int ... vals) {

В данном случае первые три аргумента, указанные в обращении к методу dolt (), соответствуют первым трем параметрам. Все остальные аргументы считаются принадлежащими параметру vals.

Помните, что параметр vararg должен быть последним. Например, следующее объявление записано неправильно:

int dolt (int a, int b, double с, int ... vals, boolean stopFlag) {
// Ошибка!

В этом примере предпринимается попытка объявления обычного параметра после параметра типа vararg, что недопустимо.

Существует еще одно ограничение, о котором следует знать: метод должен содержать только одни параметр типа varargs. Например, следующее объявление также неверно:

int dolt (int a, int b, double с, int ... vals, double ... morevals) {
// Ошибка!

Попытка объявления второго параметра типа vararg недопустима. Рассмотрим измененную версию метода vaTest (), которая принимает обычный аргумент и аргумент переменной длины:

// Использование- аргумента переменой длины совместно со стандартными аргументами.
class Var*Args2 {
//В этом примере msg — обычный параметр
// a v — параметр vararg.
static void vaTest(String msg, int . . . v) {
System.out.print(msg + v.length + " Содержимое: ");
for(int x : v)
System.out.print(x + " ") ;
System.out.println ();
}
public static void main(String args[]) {
vaTest("Один параметр vararg: ", 10);
vaTest("Три параметра vararg: ", 1, 2, 3) ;
vaTest("Be3 параметров vararg: ");
}
}

Вывод это программы имеет вид:

Один параметр vararg: 1
Содержимое: 10
Три параметра vararg: 3
Содержимое: 12 3
Вез параметров vararg: 0
Содержимое: