Чи можу я передати масив як аргументи методу зі змінними аргументами в 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
Щоправда, це можна було легко перевірити. однак, приємне в такому питанні, що воно розкриває тему та вимагає цікавих відповідей.
akf

2
Я фактично спробував код вище, маючи намір передавати аргументи масиву як аргументи методу. Однак я не усвідомлював, що повинен був би попередньо додати extraVar до аргументів. Коли ви знаєте, що змінні аргументи трактуються як масив (навіть поза методом), це, звичайно, цілком логічно.
користувач362382

Відповіді:


181

Основним типом варіативного методу function(Object... args) є function(Object[] args) . Sun додав вараги таким чином, щоб зберегти зворотну сумісність.

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


Передача аргументу типу 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.
Люк Хатчісон,

318

Так, a T...- це лише синтаксичний цукор для a T[].

JLS 8.4.1 Параметри форматування

Останній формальний параметр у списку є спеціальним; це може бути змінний параметр arity, позначений еліпсісом за типом.

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

Ось приклад для ілюстрації:

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[]. Крім того, оскільки масиви є коваріантними, а String[]- це Object[], тому ви також можете телефонувати в ezFormat(args)будь-якому випадку.

Дивитися також


Varargs gotchas №1: проходження null

Як вирішуються вараги, досить складно, і іноді це робить те, що може вас здивувати.

Розглянемо цей приклад:

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!!!

Через те, як розв’язуються вараги, останній оператор посилається на 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 ]"

Через те, як працюють вараги, 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 gotchas # 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 ... ускладнює використання додаткових підписів, оскільки компілятор виявляє неоднозначність між підписами, де навіть примітивний аргумент може бути автокопійований до Object.
вугор ghEEz

7
Є відсутність gotcha: якщо у вашого методу є останній параметр типу Object ... і ви називаєте його, наприклад, передаючи String []. Компілятор не знає, чи є ваш масив першим елементом varargs чи є еквівалентом усіх varargs. Це попередить вас.
Snicolas

Передача аргументу типу 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;

А потім передав аргумент obj як varargs. Це спрацювало.


2
Будь ласка, додайте і це.
Mathews Sunny
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.