Автор
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 Уровень 29
22 ноября 2023
В игре Space Invaders фигурирует эта штука, там vargargs массивов😁
YesOn Уровень 13
8 ноября 2022
Достойная статья про "загадочное" троеточие в методе) Спасибо, это было очень полезно 🙂👍
Макс Дудин Уровень 41
31 июля 2021
всё таки я про него прочитал (27 уровень) лучше поздно чем никогда...
fog Уровень 18
28 мая 2021
"Написать void метод, который принимает ряд целых чисел и возвращает их среднее арифметическое." Шта?!
Wiun Уровень 16
14 апреля 2021
подскажите по последнему. верно сделал?)

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
Сумма чисел от 1 до 100 = 5050, следовало бы или в varArg с 0 последовательность начинать или заполнять цикл с 1, внутренний перфекционист недоволен
Михаил Уровень 36
4 декабря 2020
Использование примеров со стримами только больше запутывает, чем вносит ясность об использовании varargs.
Иван Борзов Уровень 22
23 мая 2020
В домашнем задании номер 1 косяк: "написать void метод, который... возвращает среднее арифметическое". Void метод по определению ничего не возвращает)
Роман Кончалов Уровень 28 Expert
23 мая 2020
Мне кажется немного неуместным использование Stream API, появившегося в Java 8, в примерах про Varargs, появившегося в Java 5. Дело даже не в хронологии, а в намного большей сложности Stream API.
Денис Давыдов Уровень 38
23 мая 2020
В статье в слове "многоточие" опечатка.