Czy mogę przekazać tablicę jako argumenty do metody ze zmiennymi argumentami w Javie?

276

Chciałbym móc stworzyć funkcję, taką jak:

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

Problem polega na tym, że argsjest traktowany jak Object[]w metodzie myFormat, a tym samym jest jednym argumentem String.format, natomiast chciałbym każdy Objectw argsktóre mają być przekazane jako nowy argument. Ponieważ String.formatjest to również metoda ze zmiennymi argumentami, powinno to być możliwe.

Jeśli nie jest to możliwe, czy istnieje metoda taka jak String.format(String format, Object[] args)? W tym przypadku można ja dołączana extraVardo argsstosując nową tablicę i przekazać go do tego sposobu.

użytkownik362382
źródło
8
Nie mogę się nie zastanawiać, dlaczego to pytanie brzmi „czy to ważne”. Nie mogłeś tego po prostu wypróbować? Nie przesadzaj pytając, wyrządzisz sobie więcej szkody niż pożytku.
kamasheto
21
prawdą jest, że można to łatwo przetestować. miłą rzeczą w takim pytaniu jest to, że eksponuje temat i prosi o interesujące odpowiedzi.
akf
2
Właściwie wypróbowałem powyższy kod, z zamiarem przekazania argumentów tablicy jako argumentów do metody. Jednak nie zdawałem sobie sprawy, że najpierw powinienem był dodać extraVar do argumentów. Kiedy wiesz, że argumenty zmiennych są traktowane jak tablica (nawet poza metodą), jest to oczywiście dość logiczne.
user362382

Odpowiedzi:

181

Podstawowym typem metody variadic function(Object... args) jest function(Object[] args) . W ten sposób firma Sun dodała varargs, aby zachować kompatybilność wsteczną.

Więc powinieneś być w stanie po prostu przygotować extraVarsię argsi zadzwonić String.format(format, args).

jasonmp85
źródło
Przekazanie argumentu typu X[]do metody x(X... xs)daje następujące ostrzeżenie w środowisku 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.
Luke Hutchison
318

Tak, a T...jest tylko cukrem syntaktycznym dla T[].

JLS 8.4.1 Parametry formatu

Ostatni parametr formalny na liście jest specjalny; może to być zmienny parametr arity , wskazany przez elipsis następujący po typie.

Jeśli ostatnim parametrem formalnym jest zmienny parametr arity typu T, rozważa się zdefiniowanie formalnego parametru typu T[]. Metoda ta jest wówczas metodą zmienną . W przeciwnym razie jest to metoda o ustalonej wartości . Wywołania metody zmiennej arity mogą zawierać więcej rzeczywistych wyrażeń argumentów niż parametrów formalnych. Wszystkie rzeczywiste wyrażenia argumentów, które nie odpowiadają formalnym parametrom poprzedzającym zmienny parametr arity, zostaną ocenione, a wyniki zapisane w tablicy, która zostanie przekazana do wywołania metody.

Oto przykład ilustrujący:

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

I tak, powyższa mainmetoda jest poprawna, ponieważ znowu String...jest słuszna String[]. Ponadto, ponieważ tablice są kowariantne, a String[]jest an Object[], więc możesz także zadzwonić w ezFormat(args)obie strony.

Zobacz też


Varargs gotchas # 1: pasujący null

Sposób rozwiązywania vararg jest dość skomplikowany i czasami robi rzeczy, które mogą cię zaskoczyć.

Rozważ ten przykład:

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

Ze względu na to, jak varargs są rozwiązane, ostatnie wywołuje Oświadczenie objs = null, które oczywiście mogłyby spowodować NullPointerExceptionz objs.length. Jeśli chcesz podać jeden nullargument parametrowi varargs, możesz wykonać jedną z następujących czynności:

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

Powiązane pytania

Poniżej znajduje się próbka niektórych pytań zadawanych przez ludzi podczas obsługi varargs:


Vararg gotchas # 2: dodając dodatkowe argumenty

Jak się dowiedziałeś, następujące „nie działa”:

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

Ze względu na sposób działania varargs, ezFormatfaktycznie dostaje 2 argumenty, pierwszy to a String[], a drugi to String. Jeśli przekazujesz tablicę do varargs i chcesz, aby jej elementy były rozpoznawane jako pojedyncze argumenty, a także musisz dodać dodatkowy argument, nie masz innego wyjścia, jak stworzyć inną tablicę, która pomieści dodatkowy element.

Oto kilka przydatnych metod pomocniczych:

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

Teraz możesz wykonać następujące czynności:

    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: przekazując szereg prymitywów

To nie „działa”:

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

Varargs działa tylko z typami referencyjnymi. Autoboxing nie dotyczy tablicy prymitywów. Następujące prace:

    Integer[] myNumbers = { 1, 2, 3 };
    System.out.println(ezFormat(myNumbers));
    // prints "[ 1 ][ 2 ][ 3 ]"
środki smarujące wielotlenowe
źródło
2
Korzystanie z różnych parametrów Object ... utrudnia stosowanie dodatkowych podpisów, ponieważ kompilator identyfikuje niejednoznaczność między podpisami, w których nawet prymitywny argument może zostać automatycznie przekierowany do Object.
węgorz ghEEz
7
Brakuje gotcha: jeśli twoja metoda ma ostatni parametr typu Object ... i nazywasz ją przekazując na przykład String []. Kompilator nie wie, czy tablica jest pierwszym elementem varargs, czy też jest odpowiednikiem wszystkich varargs. Ostrzeże cię.
Snicolas,
Przekazanie argumentu typu X[]do metody x(X... xs)daje następujące ostrzeżenie w środowisku 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.
Luke Hutchison
23

Przekazanie tablicy jest w porządku - w rzeczywistości oznacza to to samo

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

jest taki sam jak

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

To tylko cukier syntaktyczny - kompilator konwertuje pierwszy na drugi, ponieważ podstawowa metoda oczekuje tablicy parametru vararg .

Widzieć

MDMA
źródło
4

jasonmp85 ma rację, mówiąc o przekazaniu innej tablicy String.format. Rozmiar tablicy nie może zostać zmieniony po zbudowaniu, więc trzeba będzie przekazać nową tablicę zamiast modyfikować istniejącą.

Object newArgs = new Object[args.length+1];
System.arraycopy(args, 0, newArgs, 1, args.length);
newArgs[0] = extraVar; 
String.format(format, extraVar, args);
ebelisle
źródło
1

Miałem ten sam problem.

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

A potem przekazał obj jako argument varargs. Zadziałało.

Srijit Paul
źródło
2
Proszę też to dodać.
Mathews Sunny,