Могу ли я передать массив в качестве аргументов в метод с переменными аргументами в 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использовать новый массив и передать его этому методу.


8
Я не могу не задаться вопросом, почему этот вопрос является «действительно ли это» такой вопрос. Не могли бы вы просто попробовать? Не переусердствуйте, спрашивая, вы принесете себе больше вреда, чем пользы.
— Камашето

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

2
Я на самом деле пробовал приведенный выше код с намерением передать аргументы массива в качестве аргументов метода. Тем не менее, я не понимал, что должен был сначала добавить extraVar к аргументам. Когда вы знаете, что переменные аргументы обрабатываются как массив (даже вне метода), это, конечно, вполне логично.
— user362382

Ответы:


181

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

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


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 ]"

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);

1

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

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

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


2
Пожалуйста, добавьте это тоже.
— Мэтьюз Санни
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.