Автор
John Selawsky
Senior Java-разработчик и преподаватель в LearningTree

Java varargs

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

Что такое varargs?

Varargs — это аргументы переменной длины: фича, которая появилась еще в JDK5. Varargs позволяют нам создавать методы с произвольным количеством аргументов. По большому счету, создавать такие методы можно было и раньше. Правда, делать это было не очень удобно. Приведем пример. Допустим, нам необходимо написать метод, который будет принимать произвольное количество целочисленных аргументов и складывать их вместе. Java varargs - 1У нас есть два варианта. Вариант 1 — перегрузка:
class Calculator {
    int sum(int a, int b){...};
    int sum(int a, int b, int c){...};
    int sum(int a, int b, int c, int d){...};
    int sum(int a, int b, int c, int d, int e){...};
}
Но с перегрузкой возникают две проблемы. Во-первых, не всегда понятно, когда пора остановиться, а во-вторых, это громоздко. Лучше уж массивы. Вариант 2 — массивы:
class Calculator {
    int sum(int[] numbers){...};
}
С массивом уже не громоздко, и вроде бы массив выглядит ничего так… до тех пор, пока не придет пора вызвать метод:
public static void main(String... sss) {
    Calculator calculator = new Calculator();

    int[] arguments = new int[7];
    arguments[0] = 1;
    arguments[1] = 10;
    arguments[2] = 123;
    arguments[3] = 234;
    arguments[4] = 6234;
    arguments[5] = 12;
    arguments[6] = 8;

    int sum = calculator.sum(arguments);
}
Согласны, можно, конечно, и короче все записать. Но во-первых, хочется все-таки продемонстрировать неудобство использования массивов в качестве аргументов переменной длины, а во-вторых, даже если мы запишем так:
int[] arguments = {1,10,123,234,6234,12,8};
int sum = calculator.sum(arguments);
То мы все равно не избавимся излишнего количества кода. Однако с выходом Java 5 для решения этих проблем появилась фича Varargs. Она позволила передавать в методы произвольное количество аргументов. Выглядит это примерно так:
public class Calculator {
    public static void main(String... sss) {
        Calculator calculator = new Calculator();
        int sum = calculator.sum(1,10,123,234,6234,12,8);
    }
    int sum(int... numbers){
       return Arrays.stream(numbers).sum();
    }
}
Итак, резюмируем. Varargs — это аргументы переменной длины, фича, которая появилась с выходом Java 5. Далее более подробно рассмотрим некоторые правила работы с Varargs.

5 правил varargs

Правило 1. Vararg аргумент (или же аргумент переменной/произвольной длины) обозначается через троеточие следующим образом:
String... words
Integer... numbers
Person... people
Cat... cats
Правило 2. Аргумент произвольной длины может быть указан только как аргумент некоторого метода:
void print(String... words)
int sum(Integer... numbers)
void save(Person... people)
void feed(Cat... cats)
Правило 3. Каждый такой аргумент переменной длины в теле метода является массивом:
void print(String... words){
    for (int i = 0; i < words.length; i++) {
        System.out.println(words[i]);
    }
}
Правило 4. Vararg аргумент должен быть последним в списке аргументов метода:
void print(String... words, String anotherWord) // - Так нельзя!
void print(String... words, int someNumber) // - Так нельзя!

void print(String anotherWord, String... words) // - Так можно
void print(int someNumber, String... words) // - Так можно
Правило 5. Несмотря на то, что varargs являются массивами, при вызове метода, который принимает аргументы переменной длины, не обязательно создавать массив. Достаточно и даже желательно просто перечислить необходимые аргументы через запятую:
public class Main {
    public static void main(String... sss) {
        print("Как","же","прекрасно","изучать","Java");
    }

    static void print(String... words){
        for (int i = 0; i < words.length; i++) {
            System.out.println(words[i]);
        }
    }
}

Примеры varargs

В примере ниже мы напишем метод, который принимает varargs состоящий из целых чисел и выводит на экран количество переданных элементов и их сумму. Передадим в этот метод и массив, и ряд целых чисел (оба варианта допустимы):
public static void main(String... sss) {
    int[] a = new int[100];
    for (int i = 0; i < a.length; i++) {
        a[i] = i;
    }

    sum(a);
    sum(1,2,3,4,5,6,7,8,9,10);
}

static void sum(int... numbers){
    final int length = numbers.length;
    final int sum = Arrays.stream(numbers).sum();
    final String lineSeparator = System.lineSeparator();

    System.out.printf("Кол-во элементов для сложения - %d, сумма - %d%s", length, sum, lineSeparator);
}
После запуска программа выведет:

Кол-во элементов для сложения - 100, сумма - 4950 
Кол-во элементов для сложения - 10, сумма - 55 
Стоит сказать, что метод System.out.printf также принимает varargs. Если мы взглянем на код этого метода, то убедимся в этом:
public PrintStream printf(String format, Object ... args) {
    return format(format, args);
}
Еще один широко используемый метод, принимающий varags — String.format. Его код приведен ниже:
public static String format(String format, Object... args) {
    return new Formatter().format(format, args).toString();
}

Когда использовать varargs?

Ответ на этот вопрос зависит от того, кто спрашивает. Если подобный вопрос задает клиент некоторого API, в котором есть методы с varargs, то ответ будет “использовать такие методы как можно чаще”. Для клиента кода varargs существенно упрощает жизнь, облегчая написание кода и повышая его читаемость. Однако в случае, если данный вопрос задает разработчик API, который интересуется, как часто стоит создавать методы с varargs, то ответ будет “не следует часто использовать varargs”. Varargs следует использовать только тогда, когда выгода от его использования очевидна. Также не следует перегружать методы с varargs, так как это вызовет у клиентов вашего кода затруднения в понимании, какой из перегруженных методов в действительности вызывается.

Заключение

Итак, мы разобрали еще одну тему, varargs в Java. Разобрали, что это такое. Расписали правила использования varargs. Взглянули на примеры методов с аргументами произвольной длины, а также обсудили, когда varargs лучше использовать, а когда от использования лучше воздержаться. В качестве домашнего задания можно реализовать следующие методы:
  • Написать void метод, который принимает ряд целых чисел и возвращает их среднее арифметическое.
  • Написать void метод, который принимает ряд строк и выводит на печать самое длинное слово.
  • Написать метод, который возвращает boolean и принимает первым аргументом переменную типа String, а следующим аргументом ряд boolean, переменной длины.
    Возможные значения StringAND, OR, XOR. В случае, если первый аргумент имеет какое то другое значение, метод должен бросить исключение IllegalArgumentException.
    Метод должен осуществить логическую операцию (указанную в первом аргументе ) над каждым элементом varargs аргумента и вернуть результат вычисления.
Пример аргументов и результата:
  • "AND", true, true, false — вернет false
  • "OR", false, true, false, false — вернет true
Комментарии (16)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Andrei Karavai
Уровень 30
22 ноября 2023, 21:54
В игре Space Invaders фигурирует эта штука, там vargargs массивов😁
YesOn
Уровень 13
8 ноября 2022, 22:48
Достойная статья про "загадочное" троеточие в методе) Спасибо, это было очень полезно 🙂👍
Макс Дудин
Уровень 41
31 июля 2021, 13:12
всё таки я про него прочитал (27 уровень) лучше поздно чем никогда...
YesOn
Уровень 13
8 ноября 2022, 22:50
Я вышел на него через статью про дженерики)) Прочитал сегодня на 13-м уровне) Иду по старому блоку обучения - Java Syntax (10 уровней).
Dregid Backend Developer в Сбербанк
14 декабря 2022, 11:01
Вы абсолютно правы, лучше поздно чем никогда... (37 уровень)
fog
Уровень 18
28 мая 2021, 11:38
"Написать void метод, который принимает ряд целых чисел и возвращает их среднее арифметическое." Шта?!
Александр
Уровень 111
1 октября 2022, 06:19
Скорее всего имелось введу "выводит на экран", как во втором задании.
Wiun
Уровень 16
14 апреля 2021, 10:24
подскажите по последнему. верно сделал?)
public static boolean someFields(String s, boolean... b) {
        if (s.equals("AND")) {
            for (boolean bo : b) {
                if (!bo) {
                    return false;
                }
            }
            return true;
        } else if (s.equals("OR")) {
            for (boolean bo : b) {
                if (bo) {
                    return true;
                }
            }
        } else throw new IllegalArgumentException();
        return false;
    }
и еще немного понять не могу почему в 16 строке возвращать должны фолс?
22 января 2021, 09:03
Сумма чисел от 1 до 100 = 5050, следовало бы или в varArg с 0 последовательность начинать или заполнять цикл с 1, внутренний перфекционист недоволен
Михаил Java Developer
4 декабря 2020, 13:16
Использование примеров со стримами только больше запутывает, чем вносит ясность об использовании varargs.
Иван Борзов
Уровень 24
23 мая 2020, 08:42
В домашнем задании номер 1 косяк: "написать void метод, который... возвращает среднее арифметическое". Void метод по определению ничего не возвращает)
Роман Кончалов Java Developer Expert
23 мая 2020, 07:33
Мне кажется немного неуместным использование Stream API, появившегося в Java 8, в примерах про Varargs, появившегося в Java 5. Дело даже не в хронологии, а в намного большей сложности Stream API.
Денис Давыдов
Уровень 38
23 мая 2020, 05:55
В статье в слове "многоточие" опечатка.