1. Введение
Переменные
были введены в Java 5
и обеспечивают сокращение для методов, поддерживающих произвольное количество параметров одного типа.
В этой статье мы увидим, как мы можем использовать эту основную функцию Java.
2. Перед Вараргами
До Java 5 всякий раз, когда мы хотели передать произвольное количество аргументов, нам приходилось передавать все аргументы массивом или реализовывать N методов (по одному на каждый дополнительный параметр):
public String format() { ... }
public String format(String value) { ... }
public String format(String val1, String val2) { ... }
3. Использование Вараргов
Переменные аргументы
помогают нам избежать написания стандартного кода, вводя новый синтаксис, который может автоматически обрабатывать произвольное количество параметров — используя массив под капотом.
Мы можем определить их, используя объявление стандартного типа, за которым следует многоточие:
public String formatWithVarArgs(String... values) {
// ...
}
И теперь мы можем вызвать наш метод с произвольным количеством аргументов, например:
formatWithVarArgs();
formatWithVarArgs("a", "b", "c", "d");
Как упоминалось ранее, varargs
— это массивы, поэтому нам нужно работать с ними так же, как с обычным массивом.
4. Правила
Варарги
просты в использовании. Но есть несколько правил, которые мы должны помнить:
- Каждый метод может иметь только один параметр
varargs.
- Аргумент
varargs
должен быть последним параметром
5. Загрязнение кучи
Использование varargs
может привести к так называемому загрязнению кучи . Чтобы лучше понять загрязнение кучи, рассмотрим этот метод varargs :
static String firstOfFirst(List<String>... strings) {
List<Integer> 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
безопасно тогда и только тогда, когда:
- Мы ничего не сохраняем в неявно созданном массиве. В этом примере мы сохранили
List<Integer>
в этом массиве . - Мы не позволяем ссылке на сгенерированный массив избежать выхода из метода (подробнее об этом позже)
Если мы уверены, что сам метод безопасно использует varargs, мы можем использовать @SafeVarargs
для подавления предупреждения.
Проще говоря, использование varargs
безопасно, если мы используем их для передачи переменного количества аргументов от вызывающей стороны к методу и ничего больше!
5.2. Справочник по побегу от
вараргов
Давайте рассмотрим еще одно небезопасное использование varargs
:
static <T> T[] toArray(T... arguments) {
return arguments;
}
Сначала может показаться, что метод toArray
совершенно безобиден. Однако, поскольку массив varargs передается вызывающей стороне, это нарушает второе правило безопасного использования varargs
.
Чтобы увидеть, насколько этот метод может быть опасным, давайте используем его в другом методе:
static <T> T[] returnAsIs(T a, T b) {
return toArray(a, b);
}
Затем, если мы вызовем этот метод:
String[] args = returnAsIs("One", "Two");
Мы снова получили бы ClassCastException.
Вот что происходит, когда мы вызываем метод returnAsIs
:
- Чтобы передать
a
иb
методуtoArray
, Java должен создать массив - Поскольку
Object[]
может содержать элементы любого типа, компилятор создает один - Метод
toArray
возвращает заданныйObject[]
вызывающей стороне. - Поскольку сайт вызова ожидает
String[],
компилятор пытается привестиObject[]
к ожидаемомуString[]
, следовательно,ClassCastException
Для более подробного обсуждения загрязнения кучи настоятельно рекомендуется прочитать пункт 32 книги Effective Java Джошуа Блоха .
6. Заключение
Варарги
могут убрать много шаблонов в Java.
И, благодаря их неявному автоупаковыванию в
Array
и из него, они играют роль в обеспечении безопасности нашего кода в будущем.
Как всегда, все примеры кода из этой статьи могут быть доступны в нашем репозитории GitHub .