Когда вы используете Varargs в Java?

192

Я боюсь varargs. Я не знаю, для чего их использовать.

Кроме того, опасно позволять людям передавать столько аргументов, сколько они хотят.

Какой пример контекста, который будет хорошим местом для их использования?

Гарри Айва
источник
3
Я не понимаю, почему это было бы "опасно". Это не более опасно, чем метод, вызываемый большое количество раз с разными аргументами. У вас есть проблемы со стеком? Тогда не стоит, потому что varargs отображаются в массивы, которые передаются по ссылке.
jfpoilpret
66
Просто хотел присоединиться: нет ничего плохого в том, чтобы избегать языковых функций, которые вам (пока) не совсем удобны. Намного лучше, чем использовать функции, которые вы не понимаете! ;)
Стивен
2
Бояться неизвестного - это нормально. Чтобы узнать больше о varargs читайте здесь docs.oracle.com/javase/tutorial/java/javaOO/arguments.html . Вы увидите, что вараргам нечего бояться. Varargs полезны. Знание того, как использовать varargs, дает вам возможность писать такие методы, как [PrintStream.format] (" docs.oracle.com/javase/7/docs/api/java/io/… , java.lang.Object ...)" ) :).
Разработчик Мариус Жиленас
1
Это не критика вараггов, но на самом деле есть некоторая причина «бояться» (пока вы точно не поймете ограничения вараггов); Вот почему существует аннотация @SafeVarargs. stackoverflow.com/a/14252221/1593924
Джон Кумбс

Ответы:

150

Переменные аргументы являются полезными для любого метода , который должен иметь дело с неопределенным числом объектов . Один хороший пример String.format. Строка формата может принимать любое количество параметров, поэтому вам необходим механизм для передачи любого количества объектов.

String.format("This is an integer: %d", myInt);
String.format("This is an integer: %d and a string: %s", myInt, myString);
Энди Уайт
источник
5
Параметр массива также может принимать неопределенное количество объектов, но параметр varargs обеспечивает большую гибкость и удобство в момент вызова. Вы можете написать код для построения массива и передать его, или вы можете позволить Java сделать это за вас, когда вы решите получать его в параметре varargs.
H2ONaCl
79

Хорошее практическое правило будет:

Msgstr "Использовать varargs для любого метода (или конструктора), которому нужен массив T (независимо от типа T) в качестве входных данных".

Это облегчит вызовы этих методов (нет необходимости new T[]{...}).

Вы можете расширить это правило, включив в него методы с List<T>аргументом, при условии, что этот аргумент предназначен только для ввода (т. Е. Список не изменяется методом).

Кроме того, я бы воздержался от использования, f(Object... args)потому что он движется в сторону программирования с неясными API.

В качестве примеров я использовал его в DesignGridLayout , где я могу добавить несколько JComponents за один вызов:

layout.row().grid(new JLabel("Label")).add(field1, field2, field3);

В приведенном выше коде метод add () определен как add(JComponent... components).

Наконец, реализация таких методов должна учитывать тот факт, что он может быть вызван с пустым vararg! Если вы хотите навязать хотя бы один аргумент, вы должны использовать такой уродливый трюк, как:

void f(T arg1, T... args) {...}

Я считаю этот трюк уродливым, потому что реализация метода будет менее простой, чем просто T... argsв списке аргументов.

Надеется, что это помогает прояснить вопрос о varargs.

jfpoilpret
источник
1
Вы рассматривали возможность добавления проверки предварительных условий if (args.length == 0) throw new RuntimeException("foo");? (Так как звонивший нарушал договор)
Миха Виденманн
24
Ну, цель хорошего API - предотвратить раннее использование как можно раньше, поэтому во время компиляции, когда это возможно, следовательно, рекомендация для void f(T arg1, T... args)этого всегда гарантирует, что он никогда не вызывается без аргументов, без необходимости ждать до времени выполнения.
jfpoilpret
Я думаю, что большую часть времени вызов функции только для varargs без аргументов вообще будет равнозначен тому, что он вообще ничего не будет делать. Дело в том, что предоставление аргумента функции varargs, скорее всего, не принесет существенного вреда.
WorldSEnder
Varargs полезны, но они не эквивалентны использованию массива. Варарги не подлежат перерасчету. Так что, как и дженерики, они подвержены стиранию типов, что вполне может быть неприемлемым ограничением в зависимости от работы.
Джулиан
34

Я часто использую varargs для вывода в журналы в целях отладки.

Практически каждый класс в моем приложении имеет метод debugPrint ():

private void debugPrint(Object... msg) {
    for (Object item : msg) System.out.print(item);
    System.out.println();
}

Затем, внутри методов класса, у меня есть вызовы, подобные следующим:

debugPrint("for assignment ", hwId, ", student ", studentId, ", question ",
    serialNo, ", the grade is ", grade);

Когда я убедился, что мой код работает, я закомментировал код в методе debugPrint (), чтобы журналы не содержали слишком много посторонней и нежелательной информации, но я мог оставить отдельные вызовы debugPrint () без комментариев. Позже, если я обнаружу ошибку, я просто раскомментирую код debugPrint (), и все мои вызовы debugPrint () будут повторно активированы.

Конечно, я мог бы так же легко избежать varargs и сделать следующее:

private void debugPrint(String msg) {
    System.out.println(msg);
}

debugPrint("for assignment " + hwId + ", student " + studentId + ", question "
    + serialNo + ", the grade is " + grade);

Однако в этом случае, когда я закомментирую код debugPrint (), серверу все еще приходится сталкиваться с проблемой объединения всех переменных при каждом вызове debugPrint (), даже если с результирующей строкой ничего не делается. Однако, если я использую varargs, сервер должен только поместить их в массив, прежде чем он поймет, что он им не нужен. Много времени сэкономлено.

боб
источник
4
Вы можете реализовать метод отладки в суперклассе для печати любого объекта с использованием отражения.
Луис Мартинес
12

Varargs можно использовать, когда мы не уверены в количестве аргументов, передаваемых в методе. Он создает массив параметров неопределенной длины в фоновом режиме, и такой параметр можно рассматривать как массив во время выполнения.

Если у нас есть метод, который перегружен для приема разного количества параметров, то вместо перегрузки метода разное время мы можем просто использовать концепцию varargs.

Также, когда тип параметров будет меняться, использование «Object ... test» значительно упростит код.

Например:

public int calculate(int...list) {
    int sum = 0;
    for (int item : list) {
        sum += item;
    }
    return sum;
}

Здесь косвенно массив типа int (list) передается как параметр и обрабатывается как массив в коде.

Для лучшего понимания перейдите по этой ссылке (мне очень помогло понимание этой концепции): http://www.javadb.com/using-varargs-in-java

PS: даже я боялся использовать varargs, когда не знал об этом. Но теперь я к этому привык. Как говорится: «Мы цепляемся за известное, боимся неизвестного», так что просто используйте его столько, сколько сможете, и он вам тоже понравится :)

Sarvan
источник
4
C # Эквивалентом varargs является «params». Он также делает то же самое и принимает переменное количество параметров. Смотрите это для лучшего понимания: dotnetperls.com/params
Сарван
12

Varargs - это функция, добавленная в Java версии 1.5.

Зачем использовать это?

  1. Что делать, если вы не знаете количество аргументов для метода?
  2. Что если вы хотите передать в метод неограниченное количество аргументов?

Как это работает?

Он создает массив с заданными аргументами и передает массив методу.

Пример :

public class Solution {



    public static void main(String[] args) {
        add(5,7);
        add(5,7,9);
    }

    public static void add(int... s){
        System.out.println(s.length);
        int sum=0;
        for(int num:s)
            sum=sum+num;
        System.out.println("sum is "+sum );
    }

}

Вывод :

2

сумма 12

3

сумма 21

Арун Пракаш
источник
6

У меня тоже есть страх, связанный с varargs:

Если вызывающая сторона передает явный массив методу (в отличие от нескольких параметров), вы получите общую ссылку на этот массив.

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

 Object[] args = new Object[] { 1, 2, 3} ;

 varArgMethod(args);  // not varArgMethod(1,2,3);

 args[2] = "something else";  // this could have unexpected side-effects

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

Тило
источник
1
Правильно, но этот пример кажется немного надуманным. Вероятно ли, что люди будут использовать ваш API таким образом?
jfpoilpret
2
Вы никогда не знаете ... И особенно, когда количество аргументов не жестко запрограммировано на вызывающей стороне, но также и собрано, скажем, в список в цикле, передача в массив не так уж редка.
Тило
5
Я думаю, что ваш пример использования, вообще говоря, не маловероятен. Кто-то может утверждать, что ваш API не должен использовать входной массив таким образом, и если он это делает, он должен документировать его.
Лоуренс Дол
перегрузить метод для поддержки обоих; argMethod (Object .. objList) argMethod (Object [] objList)
thetoolman
2
@thetoolman: Я не думаю, что это возможно. Это будет рассматриваться как дублирующий метод.
Тило
1

Я часто использую varargs для конструкторов, которые могут использовать какой-либо объект фильтра. Например, большая часть нашей системы, основанная на Hadoop, основана на Mapper, который обрабатывает сериализацию и десериализацию элементов в JSON и применяет несколько процессоров, каждый из которых принимает элемент содержимого и либо изменяет и возвращает его, либо возвращает ноль. отказаться.

Кевин Петерсон
источник
1

В Java-документе Var-Args совершенно очевидно использование var args:

http://docs.oracle.com/javase/1.5.0/docs/guide/language/varargs.html

об использовании говорит:

«Итак, когда вы должны использовать varargs? Как клиент, вы должны пользоваться ими всякий раз, когда API предлагает их. Важные области применения в основных API включают рефлексию, форматирование сообщений и новую функцию printf. Как разработчик API, вы должны использовать их экономно, только когда выгода действительно убедительна. Вообще говоря, вам не следует перегружать метод varargs, иначе программистам будет трудно понять, какая перегрузка вызывается ».

Сурендра
источник