Реклама

четверг, 14 августа 2014 г.

Работа с varargs в Java

Сегодня в рамках рубрики “Java для начинающих” хотелось бы рассказать о такой функциональной возможности в языке, как varargs или аргументы переменной длины. Хочу сразу отметить, что эта возможность присутствует и во многих других языках (например C++). В Java синтаксис выглядит для переменных типа String следующим образом:
public void getData(String ... data){
   //your code here...
}
Ну что же, приступим…

В языке Java эта возможность появилась начиная с JDK5. Метод, который принимает переменное число аргументов, называют методом переменной арности, или просто методом varargs.

Для чего же такой функционал может понадобиться? Вариантов множество. Самый простой - Вам нужно реализовать метод sumData(), который будет суммировать два числа. Правда, что тут сложного:

public class VarArgsTest {
   
   public static void main(String[] s){
       int x = 5;
       int y = 5;
       int res = sumData(x, y);
       System.out.println(res);
   }
   // вот он
   public static int sumData(int a, int b){
       return a+b;
   }
}

На следующий день к Вам забегает заказчик и говорит что задача изменилась, и в метод sumData() нужно передавать три параметра… нет, возможно, 4… или 10… Ну можно перегрузить метод для 3, 4 или 10 аргументов:

...
   public static int sumData(int a, int b){
       return a+b;
   }
   
   public static int sumData(int a, int b, int c){
       return a+b+c;
   }
   
   public static int sumData(int a, int b, int c, int d){
       return a+b+c+d;
   }

И компилятор это стерпит. И это даже будет работать. Но пожалейте же себя!

Если количество аргументов заведомо неизвестно, есть специальная запись - (int ... nums):

   public static int sumData(int ... nums){
       int res=0;
       for(int i:nums){
           res+=i;
       }
       return res;
   }

Как видите формат записи очень прост:

{Тип данных} … {имя переменной}

Эту переменную Java рассматривает как массив данных. Следовательно, и работать мы можем с переменной как с массивом - узнать длину, получив значение поля length (int x = nums.length), пройтись по массиву итератором(думаю, о них мы тоже вскоре поговорим), и т.д.

Теперь поговорим о том, каким образом пожно передавать данные в наш метод. Основных вариантов два:
1. Просто записывать параметры через запятую:

// x, y, z - объявленные ранее пременные типа int
int res = sumData(x, y, z);
   или
int res = sumData(x, y, z, 5, 12, 6);//лишь бы тип данных совпадал

2.  Передать массив, содержащий данные такого же типа:

//объявляем массив и сразу заполняем его данными
int[] ar = new int[]{1, 2, 5, 7, 11};
int res = sumData(ar);

Ну а если у Вас список? Здесь тоже нет ничего страшного. Чтобы передать, например, ArrayList в метод с varargs, нужно просто преобразовать его в массив:

public class VarArgsTest {
   
   public static void main(String[] s){
       //создаем для теста список, используя тип Integer, так как коллекции
       //не работают с простыми типами данных
       ArrayList<Integer> numList = new ArrayList<Integer>();
       //наполняем его
       numList.add(5);
       numList.add(7);
       numList.add(11);
       //преобразуем в массив
       Integer[] intAr = numList.toArray(new Integer[numList.size()]);
       int res = sumData(intAr);
       System.out.println(res);
   }
   //небольшая правка - изменим тип параметра varargs на Integer
   //При этом обратная совместимость сохранится
   public static int sumData(Integer ... nums){
       int res=0;
       for(int i:nums){
           res+=i;
       }
       return res;
   }
  
}

А теперь - что нельзя делать:
1. Незьзя объявлять дополнительные параметры метода ПОСЛЕ объявления параметра типа varargs. Он должен быть последним:

   // так нельзя
   public void sumData(Integer ... nums, String str){}
   // а так - можно
   public void sumData(String str, Integer ... nums){}

2. Метод должен содержать только ОДИН параметр типа varargs:

   //так тоже нельзя - только 1 vararg параметр!
   public void sumData(Integer ... nums, double ... dNums){}

На сегодня все. Творите и делайте мир лучше!

6 комментариев:

  1. Андрей, здравствуйте. Прочитал Вашу статью и решил попрактиковать, но получаю ошибку: reason: varargs mismatch; Integer[] cannot be converted to Integer при вызове метода sumValue(arr), вот код:


    public static void main(String[] args) {
    ArrayList list = new ArrayList(map.values());
    Integer[] arr = list.toArray(new Integer[list.size()]);
    sumValue(arr);
    }

    public void sumValue(Integer ... value) {
    int sum = 0;
    for (int i : value) {
    sum += i;
    }
    System.out.println(sum);
    }

    ОтветитьУдалить
    Ответы
    1. Доброго времени суток, Андрей.
      По идее Ваш код не должен скомпилироваться из-за другой ошибки - несоответствие типов данных в list (Object) и arr (Integer). Я попробовал смоделировать Ваш код:

      public class JavaTest {

      static Map map = new HashMap<>(); // предпологаю Ваши данные выглядят примерно так

      public static void main(String[] args) {
      map.put("one", 1);
      map.put("two", 2);
      ArrayList list = new ArrayList(map.values()); // указал тип в дженерике
      Integer[] arr = list.toArray(new Integer[list.size()]);
      sumValue(arr);
      }

      public static void sumValue(Integer ... value) {
      int sum = 0;
      for (int i : value) {
      sum += i;
      }
      System.out.println(sum);
      }
      }

      Вроде работает)). Если я в чем-то ошибся - пишите

      Удалить
    2. Oops... похоже проблема в оформлении кода в комментариях.

      Удалить
  2. Так лучше:

    public class JavaTest {

    static Map<String, Integer> map = new HashMap<>();

    public static void main(String[] args) {
    map.put("one", 1);
    map.put("two", 2);
    ArrayList<Integer> list = new ArrayList(map.values());
    Integer[] arr = list.toArray(new Integer[list.size()]);
    sumValue(arr);
    }

    public static void sumValue(Integer ... value) {
    int sum = 0;
    for (int i : value) {
    sum += i;
    }
    System.out.println(sum);
    }
    }

    ОтветитьУдалить
  3. Добрый день, спасибо за статью. Можете прояснить чем же отличается варарг от массива аргументов?

    ОтветитьУдалить
  4. >>Теперь поговорим о том, каким образом "п"ожно передавать данные в наш метод. Основных вариантов два:<< - опечатка.

    ОтветитьУдалить