2013-01-09 14 views
136

Niedawno natknąłem się na adnotację java @SafeVarargs. Googlowania za to, co sprawia, że ​​o zmiennej liczbie argumentów funkcji w Javie niebezpieczny zostawiła mi raczej mylić (? Zatrucia sterty skasowane typy?), Więc chciałbym wiedzieć kilka rzeczy:Adnotacja Java SafeVarargs, czy istnieje standardowa lub najlepsza praktyka?

  1. Co sprawia, że ​​funkcja Java w zmiennej liczbie argumentów niebezpieczne @SafeVarargs sens (najlepiej wyjaśniony w formie dogłębnego przykładu)?

  2. Dlaczego ta adnotacja jest pozostawiona do decyzji programisty? Czy nie jest to coś, co kompilator powinien sprawdzić?

  3. Czy jest jakiś standard, do którego należy się stosować, aby mieć pewność, że jego funkcja rzeczywiście jest bezpieczna? Jeśli nie, jakie są najlepsze praktyki, aby to zapewnić?

+1

Czy widziałeś przykład (i wyjaśnienie) w [JavaDoc] (http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html)? – jlordo

+2

Na trzecie pytanie, jedną z praktyk jest zawsze mieć pierwszy element i inne: 'retType myMethod (Arg pierwszy, Arg ... inne)'. Jeśli nie określisz 'first', dozwolona jest pusta tablica i możesz bardzo dobrze mieć metodę o tej samej nazwie z tym samym typem zwracanym bez przyjmowania argumentów, co oznacza, że ​​JVM będzie miała trudności z określeniem, która metoda powinna być nazywa. – fge

+4

@jlordo Zrobiłem, ale nie rozumiem, dlaczego jest podany w kontekście varargs, jak można łatwo replikować tę sytuację poza funkcją varargs (zweryfikowano to, kompiluje z oczekiwanymi ostrzeżeniami bezpieczeństwa i błędami w czasie wykonywania) .. – Oren

Odpowiedz

189

1) Istnieje wiele przykładów w Internecie i na StackOverflow na temat konkretnego problemu z rodzajami i varargs. Zasadniczo, to jest, gdy masz zmienną liczbę argumentów typu typu parametru:

void foo(T... args); 

W Javie varargs to cukier syntaktyczny że ulega proste „ponownego pisania” w czasie kompilacji: parametr varargs typu X... jest konwertowany na parametr typu X[]; i za każdym razem, gdy wywoływana jest ta metoda varargs, kompilator zbiera wszystkie "zmienne argumenty" znajdujące się w parametrze varargs i tworzy tablicę tak jak new X[] { ...(arguments go here)... }.

Działa to dobrze, gdy typ varargs jest konkretny jak String.... Gdy jest to zmienna typu, taka jak T..., działa również wtedy, gdy wiadomo, że T jest konkretnym typem dla tego połączenia. na przykład jeśli powyższa metoda była częścią klasy Foo<T>, a użytkownik ma odwołanie Foo<String>, wówczas wywołanie foo na tym byłoby w porządku, ponieważ wiemy, że T jest String w tym miejscu kodu.

Jednak nie działa, gdy "wartość" T jest parametrem innego typu. W języku Java niemożliwe jest utworzenie tablicy typu komponentu typu parametru (new T[] { ... }). W związku z tym Java zamiast niego używa new Object[] { ... } (tutaj Object jest górną granicą T; gdyby górna granica była czymś innym, byłoby to zamiast Object), a następnie wyświetli ostrzeżenie kompilatora.

Co jest nie tak z tworzeniem new Object[] zamiast new T[] czy czego? Cóż, tablice w Javie znają swój typ komponentu w czasie wykonywania. Zatem przekazany obiekt tablicy będzie miał niewłaściwy typ komponentu w czasie wykonywania.

Dla prawdopodobnie najczęstszą wykorzystanie varargs, po prostu iteracyjne nad elementami, to nie jest problem (nie dbają o rodzaju wykonawczego tablicy), więc jest to bezpieczne:

@SafeVarargs 
final void foo(T... args) { 
    for (T x : args) { 
     // do stuff with x 
    } 
} 

Jednak dla wszystkiego, co zależy od typu składnika wykonawczego przekazywanej tablicy, nie będzie ona bezpieczna.Oto prosty przykład czegoś, co jest niebezpieczne i awarie:

class UnSafeVarargs 
{ 
    static <T> T[] asArray(T... args) { 
    return args; 
    } 

    static <T> T[] arrayOfTwo(T a, T b) { 
    return asArray(a, b); 
    } 

    public static void main(String[] args) { 
    String[] bar = arrayOfTwo("hi", "mom"); 
    } 
} 

Problem polega na tym, że są uzależnione od rodzaju args być T[] w celu przywrócenia go jako T[]. Ale faktycznie typ argumentu w czasie wykonywania nie jest instancją T[].

3) Jeśli metoda ma argumentu typu T... (gdzie t oznacza dowolny parametr typ), a następnie:

  • Safe: Jeśli metoda zależy tylko na tym, że elementy tablicy są przypadki od T
  • niebezpieczne: Jeśli to polega na tym, że matryca jest wystąpienie T[]

co jest zależne od rodzaju wykonawczego tablicy obejmują: powracającej jako typ T[], podając je jako argumentu do parametru typu T[], coraz rodzaj tablicy przy użyciu .getClass(), przekazując je do metod, które są uzależnione od czasu przebiegu w tablicy, jak List.toArray() i Arrays.copyOf() itp

2) Różnica Wspomniałem wyżej, że jest zbyt skomplikowany, aby można go było łatwo odróżnić automatycznie.

+4

dobrze Teraz to wszystko ma sens. dzięki. więc po prostu, aby zobaczyć, że rozumiem cię całkowicie, powiedzmy, że mamy klasę 'FOO ' czy byłoby bezpiecznie zwrócić T [] z metody varargs jako tablicę 'Somthing's? – Oren

+4

@Oren: powinno być – newacct

+15

Może warto zauważyć, że jeden przypadek, w którym jest (prawdopodobnie) zawsze poprawne użycie '@ SafeVarargs' jest wtedy, gdy jedyną rzeczą jaką robisz z tablicą jest przekazanie jej innej metody, która jest już tak opisana (na przykład: często spotykam się z pisaniem metod vararg, które istnieją, aby przekonwertować ich argumenty na listę za pomocą 'Arrays.asList (...)' i przekazać ją innej metodzie, taki przypadek zawsze może być opatrzony adnotacją '@ SafeVarargs' ponieważ 'Arrays.asList' ma adnotację). – Jules

Powiązane problemy