Могу ли я передать массив в качестве аргументов в метод с переменными аргументами в Java?

276

Я хотел бы иметь возможность создать функцию, такую ​​как:

class A {
  private String extraVar;
  public String myFormat(String format, Object ... args){
    return String.format(format, extraVar, args);
  }
}

Проблема здесь заключается в том , что argsрассматривается как Object[]в способе myFormat, и , таким образом , является единственным аргументом String.format, в то время как я хотел бы каждый Objectв argsбудет принят в качестве нового аргумента. Поскольку String.formatэто также метод с переменными аргументами, это должно быть возможно.

Если это невозможно, есть ли такой метод String.format(String format, Object[] args)? В этом случае я мог extraVarбы argsиспользовать новый массив и передать его этому методу.

user362382
источник
8
Я не могу не задаться вопросом, почему этот вопрос является «действительно ли это» такой вопрос. Не могли бы вы просто попробовать? Не переусердствуйте, спрашивая, вы принесете себе больше вреда, чем пользы.
Камашето
21
правда, это можно было легко проверить. однако, хорошая вещь о таком вопросе состоит в том, что он выставляет тему и запрашивает интересные ответы.
2010 г.
2
Я на самом деле пробовал приведенный выше код с намерением передать аргументы массива в качестве аргументов метода. Тем не менее, я не понимал, что должен был сначала добавить extraVar к аргументам. Когда вы знаете, что переменные аргументы обрабатываются как массив (даже вне метода), это, конечно, вполне логично.
user362382

Ответы:

181

Основным типом метода вариации function(Object... args) является function(Object[] args) . Sun добавила varargs таким образом, чтобы сохранить обратную совместимость.

Таким образом , вы просто должны быть в состоянии предварять extraVarк argsи вызов String.format(format, args).

jasonmp85
источник
1
Передача аргумента типа X[]в метод x(X... xs)дает следующее предупреждение в Eclipse:Type X[] of the last argument to method x(X...) doesn't exactly match the vararg parameter type. Cast to X[] to confirm the non-varargs invocation, or pass individual arguments of type X for a varargs invocation.
Люк Хатчисон
319

Да, это T...только синтаксический сахар для T[].

JLS 8.4.1 Параметры формата

Последний формальный параметр в списке является специальным; это может быть переменный параметр арности , обозначаемый elipsis после типа.

Если последний формальный параметр является переменным параметром arity типа T, считается, что он определяет формальный параметр типа T[]. Метод тогда является методом переменной арности . В противном случае это метод фиксированной арности . Вызовы метода переменной арности могут содержать больше фактических выражений аргументов, чем формальных параметров. Все фактические выражения аргументов, которые не соответствуют формальным параметрам, предшествующим параметру переменной arity, будут оценены, а результаты сохранены в массив, который будет передан в вызов метода.

Вот пример для иллюстрации:

public static String ezFormat(Object... args) {
    String format = new String(new char[args.length])
        .replace("\0", "[ %s ]");
    return String.format(format, args);
}
public static void main(String... args) {
    System.out.println(ezFormat("A", "B", "C"));
    // prints "[ A ][ B ][ C ]"
}

И да, приведенный выше mainметод действителен, потому что опять-таки String...справедлив String[]. Кроме того, поскольку массивы являются ковариантными, a String[]- это a Object[], поэтому вы также можете вызывать в ezFormat(args)любом случае.

Смотрите также


Varargs Gotchas # 1: прохождение null

Как решить varargs довольно сложно, и иногда это делает вещи, которые могут вас удивить.

Рассмотрим этот пример:

static void count(Object... objs) {
    System.out.println(objs.length);
}

count(null, null, null); // prints "3"
count(null, null); // prints "2"
count(null); // throws java.lang.NullPointerException!!!

Из-за того, как решаются varargs, последний оператор вызывается с objs = null, что, конечно, вызвало бы NullPointerExceptionс objs.length. Если вы хотите дать один nullаргумент параметру varargs, вы можете сделать одно из следующих:

count(new Object[] { null }); // prints "1"
count((Object) null); // prints "1"

Смежные вопросы

Ниже приведен пример некоторых вопросов, которые люди задавали при работе с varargs:


Vararg Gotchas # 2: добавление дополнительных аргументов

Как вы узнали, следующее не «работает»:

    String[] myArgs = { "A", "B", "C" };
    System.out.println(ezFormat(myArgs, "Z"));
    // prints "[ [Ljava.lang.String;@13c5982 ][ Z ]"

Из-за того, как работает varargs, ezFormatфактически получает 2 аргумента, первый - a String[], второй - a String. Если вы передаете массив в varargs и хотите, чтобы его элементы распознавались как отдельные аргументы, и вам также необходимо добавить дополнительный аргумент, у вас нет другого выбора, кроме как создать другой массив, который будет содержать дополнительный элемент.

Вот несколько полезных вспомогательных методов:

static <T> T[] append(T[] arr, T lastElement) {
    final int N = arr.length;
    arr = java.util.Arrays.copyOf(arr, N+1);
    arr[N] = lastElement;
    return arr;
}
static <T> T[] prepend(T[] arr, T firstElement) {
    final int N = arr.length;
    arr = java.util.Arrays.copyOf(arr, N+1);
    System.arraycopy(arr, 0, arr, 1, N);
    arr[0] = firstElement;
    return arr;
}

Теперь вы можете сделать следующее:

    String[] myArgs = { "A", "B", "C" };
    System.out.println(ezFormat(append(myArgs, "Z")));
    // prints "[ A ][ B ][ C ][ Z ]"

    System.out.println(ezFormat(prepend(myArgs, "Z")));
    // prints "[ Z ][ A ][ B ][ C ]"

Varargs получил # 3: передавая массив примитивов

Это не "работает":

    int[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ [I@13c5982 ]"

Varargs работает только со ссылочными типами. Автобокс не применяется к массиву примитивов. Следующие работы:

    Integer[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ 1 ][ 2 ][ 3 ]"
polygenelubricants
источник
2
Использование параметров Object ... variadic затрудняет использование дополнительных сигнатур, так как компилятор выявляет неоднозначность между сигнатурами, когда даже примитивный аргумент может быть автоматически помещен в Object.
угорь ghEEz
7
Отсутствует ошибка: если у вашего метода есть последний параметр типа Object ... и вы вызываете его, передавая, например, String []. Компилятор не знает, является ли ваш массив первым элементом varargs или является эквивалентом всех varargs. Это предупредит вас.
Сниколас
Передача аргумента типа X[]в метод x(X... xs)дает следующее предупреждение в Eclipse:Type X[] of the last argument to method x(X...) doesn't exactly match the vararg parameter type. Cast to X[] to confirm the non-varargs invocation, or pass individual arguments of type X for a varargs invocation.
Люк Хатчисон
23

Можно передать массив - фактически это одно и то же

String.format("%s %s", "hello", "world!");

такой же как

String.format("%s %s", new Object[] { "hello", "world!"});

Это просто синтаксический сахар - компилятор преобразует первый во второй, поскольку базовый метод ожидает массив для параметра vararg .

Видеть

МДМА
источник
4

jasonmp85 прав насчет передачи другого массива в String.format. Размер массива нельзя изменить после его создания, поэтому вам придется передавать новый массив вместо изменения существующего.

Object newArgs = new Object[args.length+1];
System.arraycopy(args, 0, newArgs, 1, args.length);
newArgs[0] = extraVar; 
String.format(format, extraVar, args);
ebelisle
источник
1

У меня была такая же проблема.

String[] arr= new String[] { "A", "B", "C" };
Object obj = arr;

А затем передал объект в качестве аргумента varargs. Это сработало.

Сриджит Пол
источник
2
Пожалуйста, добавьте это тоже.
Мэтьюз Санни