Varargs в Java

1. Въведение

Varargs бяха въведени в Java 5 и предоставят кратка ръка за методи, които поддържат произволен брой параметри от един тип.

В тази статия ще видим как можем да използваме тази основна функция на Java.

2. Преди Varargs

Преди Java 5, когато искахме да предадем произволен брой аргументи, трябваше да предадем всички аргументи в масив или да внедрим N метода (по един за всеки допълнителен параметър):

public String format() { ... } public String format(String value) { ... } public String format(String val1, String val2) { ... }

3. Използване на Varargs

Varargs ни помагат да избягваме писането на шаблонния код, като въвеждаме новия синтаксис, който може автоматично да обработва произволен брой параметри - използвайки масив под капака.

Можем да ги дефинираме с помощта на стандартна декларация за тип, последвана от елипса:

public String formatWithVarArgs(String... values) { // ... }

И сега, можем да извикаме нашия метод с произволен брой аргументи, като:

formatWithVarArgs(); formatWithVarArgs("a", "b", "c", "d");

Както споменахме по-рано, varargs са масиви, така че трябва да работим с тях точно както бихме работили с нормален масив.

4. Правила

Varargs са лесни за използване. Но има няколко правила, които трябва да имаме предвид:

  • Всеки метод може да има само един параметър varargs
  • Аргументът varargs трябва да бъде последният параметър

5. Замърсяване на купчината

Използването на varargs може да доведе до така нареченото Heap Pollution. За да разберете по-добре замърсяването на купчината, помислете за този метод на varargs :

static String firstOfFirst(List... strings) { List ints = Collections.singletonList(42); Object[] objects = strings; objects[0] = ints; // Heap pollution return strings[0].get(0); // ClassCastException }

Ако извикаме този странен метод в тест:

String one = firstOfFirst(Arrays.asList("one", "two"), Collections.emptyList()); assertEquals("one", one);

Бихме получили ClassCastException, въпреки че дори не използвахме никакви изрични отливки тук:

java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String

5.1. Безопасно използване

Всеки път, когато използваме varargs , Java компилаторът създава масив, който да съхранява дадените параметри. В този случай компилаторът създава масив с компоненти от общ тип, за да съдържа аргументите.

Когато използваме varargs с общи типове, тъй като съществува потенциален риск от фатално изключение по време на изпълнение, Java компилаторът ни предупреждава за възможна опасна употреба на varargs :

warning: [varargs] Possible heap pollution from parameterized vararg type T

Използването на varargs е безопасно, ако и само ако:

  • Не съхраняваме нищо в неявно създадения масив. В този пример съхранихме Списък в този масив
  • Не позволяваме на препратката към генерирания масив да избяга от метода (повече за това по-късно)

Ако сме сигурни, че самият метод използва безопасно varargs, можем да използваме @SafeVarargs за потискане на предупреждението.

Казано по-просто, използването на varargs е безопасно, ако ги използваме за прехвърляне на променлив брой аргументи от повикващия към метода и нищо повече!

5.2. Справка за бягство от Varargs

Нека разгледаме друго небезопасно използване на varargs :

static  T[] toArray(T... arguments) { return arguments; }

Отначало може да изглежда, че методът toArray е напълно безвреден. Тъй като обаче позволява на масива varargs да избяга до повикващия, той нарушава второто правило за безопасни varargs .

За да видим как този метод може да бъде опасен, нека го използваме в друг метод:

static  T[] returnAsIs(T a, T b) { return toArray(a, b); }

Тогава, ако извикаме този метод:

String[] args = returnAsIs("One", "Two");

Отново бихме получили ClassCastException. Ето какво се случва, когато извикаме метода returnAsIs :

  • За да предаде a и b на метода toArray , Java трябва да създаде масив
  • Тъй като Object [] може да съдържа елементи от всякакъв тип, компилаторът създава такъв
  • Методът toArray връща дадения обект [] на повикващия
  • Тъй като сайтът за повикване очаква String [], компилаторът се опитва да прехвърли Object [] към очаквания String [] , следователно ClassCastException

За по-подробна дискусия относно замърсяването на купчините, силно се препоръчва да прочетете точка 32 от Ефективна Java от Джошуа Блок.

6. Заключение

Varargs може да накара много образци да изчезнат в Java.

И благодарение на техния имплицитен автобокс към и от Array, те играят роля в бъдещата проверка на нашия код.

Както винаги, всички примери за кодове от тази статия могат да бъдат налични в нашето хранилище на GitHub.