Перейти к основному содержимому

Варарги на Яве

· 4 мин. чтения

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 .