Kiedy używasz varargs w Javie?

192

Boję się varargs. Nie wiem do czego ich używać.

Ponadto niebezpieczne jest pozwalanie ludziom przekazywać tyle argumentów, ile chcą.

Jaki jest przykład kontekstu, który byłby dobrym miejscem do ich użycia?

Harry Quince
źródło
3
Nie rozumiem, dlaczego byłoby to „niebezpieczne”. Nie jest to bardziej niebezpieczne niż metoda wywoływana wiele razy z różnymi argumentami. Czy masz obawy dotyczące stosu? Zatem nie powinieneś, ponieważ varargs są mapowane na tablice, które są przekazywane przez referencję.
jfpoilpret
66
Chciałem tylko wejść: nie ma nic złego w unikaniu funkcji językowych, z którymi nie jesteś (jeszcze) w pełni komfortowy. O wiele lepsze niż korzystanie z funkcji, których nie rozumiesz! ;)
Steven
2
Lęk przed nieznanym jest czymś normalnym. Aby dowiedzieć się więcej, przeczytaj o varargs tutaj docs.oracle.com/javase/tutorial/java/javaOO/arguments.html . Przekonasz się, że varargs nie mają się czego bać. Przydatne są Varargs. Wiedza na temat korzystania z varargs umożliwia pisanie metod takich jak [PrintStream.format] („ docs.oracle.com/javase/7/docs/api/java/io/… , java.lang.Object ...)” ) :).
Deweloper Marius Žilėnas,
1
Nie jest to krytyka varargs, ale w rzeczywistości istnieje jakiś powód, aby „bać się” (dopóki nie zrozumiesz dokładnie ograniczeń varargs); dlatego istnieje adnotacja @SafeVarargs. stackoverflow.com/a/14252221/1593924
Jon Coombs

Odpowiedzi:

150

Varargsprzydatne w każdej metodzie, która musi poradzić sobie z nieokreśloną liczbą obiektów . Dobrym przykładem jest String.format. Ciąg formatu może zaakceptować dowolną liczbę parametrów, dlatego potrzebny jest mechanizm do przekazywania dowolnej liczby obiektów.

String.format("This is an integer: %d", myInt);
String.format("This is an integer: %d and a string: %s", myInt, myString);
Andy White
źródło
5
Parametr tablicowy może również odbierać nieokreśloną liczbę obiektów, ale parametr varargs zapewnia większą elastyczność i wygodę w miejscu wywołania. Możesz napisać kod, aby zbudować tablicę i przekazać ją, lub możesz pozwolić, aby Java zrobiła to za ciebie, gdy zdecydujesz się otrzymać go w parametrze varargs.
H2ONaCl,
79

Dobrą zasadą byłoby:

„Użyj varargs dla dowolnej metody (lub konstruktora), która potrzebuje tablicy T (niezależnie od typu T) jako danych wejściowych”.

Ułatwi to wywołanie tych metod (nie trzeba tego robić new T[]{...}).

Możesz rozszerzyć tę regułę o metody z List<T>argumentem, pod warunkiem, że ten argument służy tylko do wprowadzania danych (tj. Lista nie jest modyfikowana przez metodę).

Ponadto powstrzymałbym się od używania, f(Object... args)ponieważ jego poślizg w kierunku programowania z niejasnymi interfejsami API.

Pod względem przykładów użyłem go w DesignGridLayout , gdzie mogę dodać kilka JComponents w jednym wywołaniu:

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

W powyższym kodzie metoda add () jest zdefiniowana jako add(JComponent... components).

Wreszcie, wdrożenie takich metod musi zadbać o to, aby można je było wywołać z pustym vararg! Jeśli chcesz narzucić co najmniej jeden argument, musisz użyć brzydkiej sztuczki, takiej jak:

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

Uważam tę sztuczkę za brzydką, ponieważ implementacja metody będzie mniej prosta niż tylko T... argsna liście argumentów.

Ma nadzieję, że pomoże to wyjaśnić punkt dotyczący varargs.

jfpoilpret
źródło
1
Czy if (args.length == 0) throw new RuntimeException("foo");zamiast tego rozważałeś dodanie kontroli warunków wstępnych ? (Ponieważ dzwoniący naruszył umowę)
Micha Wiedenmann
24
Cóż, celem dobrego API jest zapobieganie nadużyciom tak wcześnie, jak to możliwe, więc w czasie kompilacji, gdy jest to możliwe, dlatego sugeruje się, void f(T arg1, T... args)aby zawsze zapewniać, że nigdy nie zostanie wywołane bez argumentów, bez konieczności czekania do czasu wykonania.
jfpoilpret
Myślę, że przez większość czasu wywołanie funkcji wyłącznie varargs bez żadnych argumentów będzie po prostu oznaczać, że w ogóle nic nie zrobię. Chodzi o to, że brak argumentu dla funkcji varargs najprawdopodobniej nie wyrządzi znacznej szkody.
WorldSEnder
Varargs są przydatne, ale nie są równoważne z użyciem tablicy. Varargs nie podlegają zwrotowi. Podobnie jak w przypadku leków generycznych, wpływa na nie kasowanie typu, które może być niedopuszczalnym ograniczeniem w zależności od zadania.
Julian
34

Często używam varargsa do wysyłania danych do dzienników w celu debugowania.

Prawie każda klasa w mojej aplikacji ma metodę debugPrint ():

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

Następnie, w ramach metod klasy, mam wywołania takie jak następujące:

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

Kiedy jestem przekonany, że mój kod działa, komentuję kod w metodzie debugPrint (), aby dzienniki nie zawierały zbyt wielu obcych i niepożądanych informacji, ale mogę pozostawić poszczególne wywołania debugPrint () bez komentarza. Później, jeśli znajdę błąd, odkomentuję kod debugPrint () i wszystkie moje wywołania debugPrint () zostaną ponownie aktywowane.

Oczywiście mógłbym równie łatwo uniknąć varargs i zamiast tego wykonać następujące czynności:

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

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

Jednak w tym przypadku, gdy komentuję kod debugPrint (), serwer nadal musi sobie poradzić z konkatenacją wszystkich zmiennych w każdym wywołaniu debugPrint (), nawet jeśli nic nie jest zrobione z wynikowym łańcuchem. Jeśli jednak używam varargs, serwer musi tylko umieścić je w tablicy, zanim zorientuje się, że ich nie potrzebuje. Oszczędność czasu.

pion
źródło
4
Możesz zaimplementować metodę nadrzędną w nadklasie, aby wydrukować dowolny obiekt za pomocą odbicia.
Lluis Martinez
12

Varargs można zastosować, gdy nie jesteśmy pewni co do liczby argumentów przekazywanych w metodzie. Tworzy tablicę parametrów o nieokreślonej długości w tle, a taki parametr można traktować jako tablicę w czasie wykonywania.

Jeśli mamy metodę, która jest przeciążona, aby zaakceptować inną liczbę parametrów, to zamiast przeciążać metodę w różnych momentach, możemy po prostu użyć koncepcji varargs.

Również, gdy typ parametrów będzie się zmieniać, wówczas użycie „Object ... test” znacznie uprości kod.

Na przykład:

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

Tutaj pośrednio tablica typu int (lista) jest przekazywana jako parametr i jest traktowana jako tablica w kodzie.

Dla lepszego zrozumienia skorzystaj z tego linku (bardzo pomogło mi to w zrozumieniu tej koncepcji): http://www.javadb.com/using-varargs-in-java

PS: Nawet bałem się używania varargsa, kiedy go nie znałem. Ale teraz się do tego przyzwyczaiłem. Jak powiedziano: „Trzymamy się tego, co znane, boimy się nieznanego”, więc używaj go jak najwięcej, a ty też polubisz :)

Sarvan
źródło
4
Odpowiednikiem C # varargs jest „params”. Robi to samo i akceptuje zmienną liczbę parametrów. Zobacz to dla lepszego zrozumienia: dotnetperls.com/params
Sarvan
11

Varargs to funkcja dodana w wersji Java 1.5.

Dlaczego z tego korzystać?

  1. Co jeśli nie znasz liczby argumentów do przekazania dla metody?
  2. Co jeśli chcesz przekazać metodzie nieograniczoną liczbę argumentów?

Jak to działa

Tworzy tablicę z podanymi argumentami i przekazuje tablicę do metody.

Przykład:

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

}

Wynik :

2)

suma wynosi 12

3)

suma wynosi 21

Arun Prakash
źródło
6

Mam też lęk związany z varargsem:

Jeśli wywołujący przekaże do metody jawną tablicę (w przeciwieństwie do wielu parametrów), otrzymasz wspólne odwołanie do tej tablicy.

Jeśli musisz przechowywać tę tablicę wewnętrznie, możesz ją najpierw sklonować, aby rozmówca nie mógł jej później zmienić.

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

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

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

Chociaż tak naprawdę nie różni się to od przekazywania jakiegokolwiek obiektu, którego stan może się później zmienić, ponieważ tablica jest zwykle (w przypadku wywołania z wieloma argumentami zamiast tablicy) świeżo utworzona przez kompilator wewnętrznie, którą można bezpiecznie użycie, jest to z pewnością nieoczekiwane zachowanie.

Thilo
źródło
1
Zgadza się, ale ten przykład wydaje się nieco przesadny. Czy to prawdopodobne, że ludzie będą używać Twojego interfejsu API w ten sposób?
jfpoilpret
2
Nigdy nie wiadomo ... A zwłaszcza, gdy liczba argumentów nie jest na stałe zakodowana po stronie wywołującej, ale także zebranych powiedz do listy w pętli, przekazywanie tablicy nie jest niczym niezwykłym.
Thilo
5
Myślę, że twój przykład użycia nie jest ogólnie rzecz biorąc mało prawdopodobny. Można argumentować, że Twój interfejs API nie powinien używać tablicy wejściowej w ten sposób, a jeśli tak, to musi ją udokumentować.
Lawrence Dol
przeciąż metodę, aby obsługiwać oba; argMethod (Object .. objList) argMethod (Object [] objList)
thetoolman
2
@thetoolman: Nie sądzę, że jest to możliwe. Będzie to traktowane jako duplikat metody.
Thilo,
1

Często używam varargs dla konstruktorów, które mogą pobrać jakiś obiekt filtrujący. Na przykład duża część naszego systemu opartego na Hadoop jest oparta na programie Mapper, który obsługuje serializację i deserializację elementów w JSON, i stosuje szereg procesorów, z których każdy pobiera element i modyfikuje go i zwraca, lub zwraca wartość null odrzucić.

Kevin Peterson
źródło
1

W dokumentacji Java Var-Args jest całkiem jasne, że użycie var args:

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

o użyciu mówi:

„Więc kiedy powinieneś używać varargs? Jako klient powinieneś z nich korzystać, ilekroć API je oferuje. Ważne zastosowania w podstawowych API obejmują odbicie, formatowanie wiadomości i nową funkcję printf. Jako projektant API powinieneś ich używać oszczędnie, tylko wtedy, gdy korzyść jest naprawdę przekonująca. Mówiąc ogólnie, nie powinieneś przeciążać metody varargs, w przeciwnym razie programiści będą mieli trudności z ustaleniem, które wywołanie przeciążenia zostanie wywołane. ”

Surendra
źródło