Java Varargs (Variable Arguments) - Üç Noktalı Parametre Nedir? Kullanım Örnekleri

1. Varargs (Variable Arguments) Giriş

Java'da metot içindeki parametre tanımlamalarında üç nokta ne anlama geliyor diye soracak olursanız bunun varargs (Variable Arguments) olduğunu artık biliyoruz demektir.

Bir çok programlama dilinde kulllanılan ve Java tarafına Java 5 sürümü ile gelmiş bir özellik olan Varargs, rastgele sayıda değer alan bir yöntemin, tek bir parametre olarak tanımlanmasına (temelde bir dizi olarak) izin verir.

2. Varargs Kullanım Avantajı ve Ne Zaman Kullanılabilir

  • Varargs'dan önce, rastgele sayıda değer alan bir metod, uygun tip değerlerine sahip bir dizi oluşturmamızı gerektiriyordu. Ardından, tanımlanan metod argüman olarak bir dizi ile çağrılabilirdi.
 public static void main(String[] args) {
        int[] intArray = { 20, 30, 40};
        display(intArray);
    }
    
    public static void display(int[] intArray){
	//
    }

Varargs ile;

    public static void main(String[] args) {
        display(20, 30, 40);
    }
    
    public static void display(int... intArray){
	//
    }
  • Varargs, bir metotta gönderilecek argüman sayısından emin olmadığımızda kullanılabilir.
public void display(String val1, String val2, String val3, String val4,....)
public void display(String... values)
  • Metod overloading durumları için yani farklı parametrelerde ayrı metotlar tanımlamasına çözüm getirebileceği için daha az kod yazılabilir.

Method Overloading kullanım Örneği;

public String display()
 { ... }


public String display(String value)
 { ... }


public String display(String value, String value2)
{ ... }

Varargs Kullanım örneği

public String display(String... values)
{ ... }

3. Varargs Kullanımı ve Tanımlama Kuralları

Varargs'ı her kullandığımızda, Java derleyicisi gönderilen değerleri tutmak için bir dizi oluşturur.

Şu kod örneğini ele alalım;

displayStringValues(String... values)

Varargs (...) kullanımıyla derleyiciye varargs parametresinin kullanıldığı söylenir. Bunun sonucunda values değişkeni String[] values türünde bir dizi olarak tanımlanır.

3.1 Varargs parametresi

  • Temelde dizi (array) parametresidir.
  • Herhangi bir değer gönderilmeyebilir veya birden fazla değer gönderilebilir.

3.2 Varargs Tanımlama Kuralları

  • Bir metodun yalnızca bir varargs parametresi alabilir
  • Vararg parametresi ilgili metotda en son sırada tanımlanmalıdır.

4. Varargs Örneği

Örnek;

void displayStringValues(String... values) olarak varargs parametresi içeren bir metot tanımlayalım. Gönderilmiş olan kaç veri varsa bunların toplam sayısını ve değerlerini konsola yazdıralım.

    public static void displayStringValues(String... values) {
        System.out.println("Number of arguments - [values_length]: " + values.length);
        for (String value : values) {
            System.out.print(value + " ");
        }
        System.out.println(); // For output
    }

Tanımlanmış olan metodu 3 farklı şekilde çağıralım ve sonuçlarını inceleyelim.

public static void main(String[] args) {
        displayStringValues();
        displayStringValues("A");
        displayStringValues("A", "B", "1", "2");
    }

Çıktı:

Number of arguments - [values_length]: 0

Number of arguments - [values_length]: 1
A 
Number of arguments - [values_length]: 4
A B 1 2 
  • displayStringValues(); ile herhangi bir değer gönderilmedi ve yöntemdeki varargs parametresine boş bir "String[]" dizisi olarak eklendi
  • displayStringValues("A"); ile "A" değerini gönderdik ve String dizisi olarak eklemiş olduk.
  • displayStringValues("A", "B", "1", "2"); ile "A", "B", "1", "2" değerlerini String dizisi olarak göndermiş olduk. Döngü içinde değerleri de yazdırmış olduk.

5. Varargs Kullanım Örnekleri:

5.1 Aynı İsimler (Method Overloading) ve Farklı Tipte Varargs Parametreleriyle

displayData isimli iki metot tanımlayalım bunlardan biri String displayData(String... values), diğeri int tipte void displayData(int... values) olsun.

    public static void displayData(String... values) {
        System.out.println("[STRING] Number of arguments - [values.length]: " + values.length);
        for (String value : values) {
            System.out.println(value + " ");
        }
    }
String... values
    public static void displayData(int... values) {
        System.out.println("[INT] Number of arguments [values.length]: " + values.length);
        for (int value : values) {
            System.out.println(value + " ");
        }
    }
int... values

Bu metotları Main metotdan şu şekilde çağıralım; 

public static void main(String[] args) {
        displayData("A", "B");
        displayData(1, 2);
    }

Output:

[STRING] Number of arguments - [values.length]: 2
A 
B 
[INT] Number of arguments [values.length]: 2
1 
2 

Çalışma (Runtime) anında, belli tipte

  • displayData("A", "B"); ile displayData(String... values) metodu çağrıldı.
  • displayData(1, 2); ile displayData(int... values) parametreli metod çağrıldı.

Görüldüğü üzere bir sorun oluşmamaktadır.

5.1.2 - Varargs - Method Overloading'de Karmaşıklık Problemi

Yukardaki örneği farklı bir durumda değerlendireceğiz. Yukardaki örnekte olduğu gibi void displayData(int... values) ve void displayData(String... values) metotlarını tanımlayalım.

 public static void displayData(int... values) {
        System.out.println("[INT] Number of arguments [values.length]: " + values.length);
        for (int value : values) {
            System.out.println(value + " ");
        }
    }

    public static void displayData(String... values) {
        System.out.println("[STRING] Number of arguments - [values.length]: " + values.length);
        for (String value : values) {
            System.out.println(value + " ");
        }
    }

main metotdan;

public static void main(String[] args) {
        displayData();
    }

displayData(); ile çalıştırmak istediğimiz zaman;

Output:

java: reference to displayData is ambiguous
  both method displayData(int...) in Main and method displayData(java.lang.String...) in Main match

hatası alınacaktır.

Çünkü aynı isimde iki metot var ve parametresiz metot çağrımında hangi metodun çağrılacağı derleme anında (compile) karmaşıklağa (ambiguous) sebep olmaktadır.

5.1.3 Uygun Olmayan Parametrelerle

Çoğu zaman bu senaryoyu derleyici haber verse de, derleme anında anlaşılacak diğer bir hatadır.

Yukardaki aynı örnek için displayData(1,"A"); ile metot çağrımı yapıldığı zaman;

    public static void main(String[] args) {
        displayData(1,"A");
    }

Output:

java: no suitable method found for displayData(int,java.lang.String)
    method Main.displayData(int...) is not applicable
      (varargs mismatch; java.lang.String cannot be converted to int)
    method Main.displayData(java.lang.String...) is not applicable
      (varargs mismatch; int cannot be converted to java.lang.String)

İki metoda uygunluğu denedi fakat ikisinde de olan varargs parametresi bu değer atamasını gerçekleştiremedi.


5.2 Metot Overloading - Varargs ve Dizi Parametre Tanımlamaları

displayData(String... values) ve displayData(String[] values) olarak aynı isimde iki farklı metod tanımlayalım.

    public static void displayData(String... values) {
        System.out.println("[STRING] Number of arguments - [values.length]: " + values.length);
        for (String s : values) {
            System.out.println(s + " ");
        }
    }

    public static void displayData(String[] values) {
        System.out.println("[STRING] Number of arguments - [values.length]: " + values.length);
        for (String s : values) {
            System.out.println(s + " ");
        }
    }
    public static void main(String[] args) {
        displayData("A", "B");
    }
  • main metotdan displayData("A", "B"); ile çağırdığımızda aşağıdaki hata alınacaktır:

Çıktı:

java: cannot declare both displayData(java.lang.String[]) and displayData(java.lang.String...) in Main

Sebebi ise Varargs'ın bir dizi parametersi olduğunu söylemiştik

  • displayData(String[] values) -> String[] values olarak String tipte dizi parametresidir.
  • displayData(String... values) -> String... values olarak varargs parametresi olsa da aslında tutacağı değer String tipte dizi değişkenidir.

5.3 Varargs Çoklu Paramatre Kullanımı

Varargs ile ilgili kuralları hatırlamak gerekirse;

Bir metodun yalnızca bir varargs parametresi alabilir
Vararg parametresi ilgili metotda en son sırada tanımlanmalıdır.

5.3.1 Varargs Çoklu Parametre Örneği

İki kurala uygun bir şekilde, displayData(int intVal, int... values) metodunu tanımlayalım.

  • İlk parametresi int intVal, ikinci parametresi ise int... values olarak bir vararg parametresi.
    public static void displayData(int intVal, int... values) {
        System.out.println("intVal value: " + intVal);
        System.out.println("[INT] Number of arguments [values.length]: " + values.length);
        for (int value : values) {
            System.out.println(value + " ");
        }
    }

main metoduyla ilgili metodu çağıralım.

    public static void main(String[] args) {
        displayData(10);
    }
intVal value: 10
[INT] Number of arguments [values.length]: 0
  • displayData(10); ile ilk parametreye değer geçildi ve ikinci parametre ise boş bir dizi olarak kaldı.

Aynı metodu displayData(1, 2, 3, 4); olarak çağıralım;

    public static void main(String[] args) {
        displayData(1, 2, 3, 4);
    }

Çıktı:

Val1 value: 1
[INT] Number of arguments [values.length]: 3
2 
3 
4 

displayData(int intVal, int... values) metodu displayData(1, 2, 3, 4); ile çağrıldığı zaman;

  • Parametre sırasına göre, "1" değeri "intVal" parametresine gönderildi.
  • "2", "3", "4" int değerleri ise dizi şeklinde int... values varargs parametresine eklendi.

5.3.2 Varargs Çoklu Parametre Örneği - 2

5.3.1 örneğine benzer olarak, displayData(int intVal, String strVal, int... values) metodunu tanımlayalım.

    public static void displayData(int intVal, String strVal, int... values) {
        System.out.println("Val1 value: " + intVal);
        System.out.println("Val1 value: " + strVal);

        System.out.println("[INT] Number of arguments [values.length]: " + values.length);
        for (int value : values) {
            System.out.println(value + " ");
        }
    }
    public static void main(String[] args) {
        displayData(10, "Volkan");

    }

ile çıktı:

intVal value: 10
strVal value: Volkan
[INT] Number of arguments [values.length]: 0

Aynı metodu;

    public static void main(String[] args) {
        displayData(10, "Volkan", 100, 1000);
    }

olarak çağırdığımızda çıktı:

intVal value: 10
strVal value: Volkan
[INT] Number of arguments [values.length]: 2
100 
1000 

Not: Vararg parametresinden önce, birden fazla farklı tipte (String, int, long vs) değişken tanımlaması yapılabilir.  Kuraldan hatırlayacağımız üzere, varargs parametresi metotda en son sıraya eklenmelidir. Daha önceki örneklerde de görebileceğimiz üzere, vararg parametresine değer göndermek zorunlu değildir.


5.4 Method Overloading - Çoklu Parametre Kullanımı

displayData isminde, displayData(int... values) ve displayData(int val1, int... values) metotlarını tanımlayalım.

    public static void displayData(int... values) {
        System.out.println("[INT] Number of arguments [values.length]: " + values.length);
        for (int i : values) {
            System.out.println(i + " ");
        }
    }


    public static void displayData(int intVal, int... values) {
        System.out.println("Val1 value: " + intVal);
        System.out.println("[INT] Number of arguments [values.length]: " + values.length);
        for (int value : values) {
            System.out.println(value + " ");
        }
    }

main metotdan,displayData(); ile çağıralım.

    public static void main(String[] args) {
        displayData();
    }

Output:

[INT] Number of arguments [values.length]: 0

Bir sorun oluşmadı çünkü:

  • Parametresiz gönderdiğimiz içindisplayData(int... values) metodu uygun bir metotdu ve çağrıldı.

Fakat eğer;

    public static void main(String[] args) {
        displayData(1);
    }

veya

    public static void main(String[] args) {
        displayData(10, 20, 30, 40);
    }

ile çağırmaya çalışırsak:

java: reference to displayData is ambiguous
  both method displayData(int...) in Main and method displayData(int,int...) in Main match

Derleme anında (compile) ilgili hata alınacaktır.

displayData(1) ile çağrıldığında, Gönderilen 1 değeri;

  • displayData(int intVal, int... values) için "1" değeri intVal parametresine uygun bir değer geçişidir. Varargs parametresi için değer göndermek zorunlu değildir.
  • displayData(int... values) metoduna da uygun bir değer geçişidir.

displayData(10, 20, 30, 40);  şeklinde çağrıldığında gönderilen (10, 20, 30, 40) değerleri;

  • displayData(int intVal, int... values) için 10 değeri intVal parametresine, 20 ve 30 ve 40'ı Varargs parametresi şeklinde uygun değer geçişleridir.
  • displayData(int... values) metoduna da uygun değer geçişleridir.

Bu sebeple derleme anında, ambigious hatası alınmaktadır.

Bu sorunu çözmek için;

Bir int dizisi (array) tanımlayıp, varargs parametresine o diziyi göndermek sorunu çözecektir.

    public static void main(String[] args) {

        int[] intArray = { 20, 30, 40};
        displayData(10, intArray);

    }

Çıktı:

Val1 value: 10
[INT] Number of arguments [values.length]: 3
20 
30 
40 
  • int[] intArray = { 20, 30, 40}; int tipinde bir dizi tanımladık.
  • displayData(10, intArray); ile uygun bir metot çağrımı yaptık.
  • Varargs parametresi ilgili tipten bir dizi olduğundan, eşleşilecek metot displayData(int val1, int... values) olacaktır.

Derleme anındaki karmaşıklık sorunu bu şekilde çözülmüş oldu.


5.5 Dizi ve VarArg Parametresinin Birlikte Kullanımı

displayData(int[] intArray, int... values) olarak bir metot tanımlayalım.

    public static void displayData(int[] intArray, int... values) {
        System.out.println("[INT] Number of arguments [values.length]: " + values.length);
        for (int value : values) {
            System.out.println(value + " ");
        }
        System.out.println("[INT] Number of arguments [intArray.length]: " + intArray.length);
        for (int value : intArray) {
            System.out.println(value + " ");
        }
    }
5.5.1 Çağrım Örneği - 1

Main metot içinde

  public static void main(String[] args) {

        int[] intArr = {1, 2};
        displayData(intArr);

    }

Çıktı;

[INT] Number of arguments [values.length]: 0
[INT] Number of arguments [intArray.length]: 2
1 
2 

displayData(int[] intArray, int... values) metoduna displayData(intArr); çağrımı ile

  • intArr dizisi tanımlı olan metodunda ilk parametreye gönderildi.  
5.5.2 Çağrım Örneği - 2
    public static void main(String[] args) {

        int[] intArr = {1, 2};
        displayData(intArr, 10, 20, 30);

    }

Çıktı:

[INT] Number of arguments [values.length]: 3
10 
20 
30 
[INT] Number of arguments [intArray.length]: 2
1 
2 

displayData(int[] intArray, int... values) metoduna displayData(intArr, 10, 20, 30); çağrımı ile;

  • intArr dizisi tanımlı olan  metodunda ilk parametreye gönderildi.  
  • 10, 20, 30 değerleri ise ikinci sırada olan  int... values vararg parametresine int dizisi şeklinde eklenmiş oldu.
5.5.3 Çağrım Örneği - 3
public static void main(String[] args) {

        int[] intArr = {1, 2};
        displayData(intArr, intArr);

    }

Çıktı;

[INT] Number of arguments [values.length]: 2
1 
2 
[INT] Number of arguments [intArray.length]: 2
1 
2 

displayData(int[] intArray, int... values) metoduna displayData(intArr, intArr); çağrımı ile;

  • intArr dizisi tanımlı olan  metodunda ilk parametreye gönderildi.  
  • intArr dizisi aynı şekilde ikinci sırada olan  int... values vararg parametresine gönderilmiş oldu.

5.6 İki Boyutlu Dizi ile VarArgs Kullanımı

displayData(int... values) ve displayData(int[]... values) olarak iki farklı metot tanımlayalım.

public static void displayData(int... values) {
        System.out.println("displayData(int... values)");
        for (int intVal : values)
            System.out.println(intVal);
    }

    public static void displayData(int[]... values) {
        System.out.println("displayData(int[]... values)");
        for (int[] array : values)
            for (int intVal : array)
                System.out.println(intVal);
    }

Main metot içinde üç tek boyutlu dizi, bir tane de çift boyutlu dizi tanımlayıp displayData metoduna gönderim yapalım.

 public static void main(String[] args) {
        int[] arr1 = {1, 2};
        int[] arr2 = {20};
        int[] arr3 = {100, 200};

        displayData(arr1);
        System.out.println("-----------");
        displayData(arr1, arr2);
        System.out.println("-----------");
        displayData(arr1, arr2, arr3);

        System.out.println("-----------");
        int[][] arr4 = {{1000, 2000}, {3000}};
        displayData(arr4);
        
    }

Output;

displayData(int... values)
1
2
-----------
displayData(int[]... values)
1
2
20
-----------
displayData(int[]... values)
1
2
20
100
200
-----------
displayData(int[]... values)
1000
2000
3000

Process finished with exit code 0

Çıktıdan da görüleceği üzere;

  • Sadece  displayData(arr1); çağrımı displayData(int... values) metoduna uygundu.
  • displayData(arr1, arr2); ve displayData(arr1, arr2, arr3); metot çağrımları çift boyutlu dizi olarakdisplayData(int[]... values)  metoduna uygundu.
  • Aynı şekilde çift boyutlu dizi tanımlamasını displayData(arr4); ile çağrım yaptığımızda, displayData(int[]... values) metoduna uygundu.

5.7 Varargs - Primitive ve Object - Wrapper Class - Metot Overloading Kullanımı

5.7.1 Örnek 1

public static void displayData(int... values) ve public static void displayData(Integer... values) olarak iki fonksiyon tanımlayalım.

  public static void displayData(int... values) {
        System.out.println("[INT] Number of arguments [values.length]: " + values.length);
        for (int value : values) {
            System.out.println(value + " ");
        }
    }

    public static void displayData(Integer... values) {
        System.out.println("[INTEGER] Number of arguments [values.length]: " + values.length);
        for (int value : values) {
            System.out.println(value + " ");
        }
    }

Main Metot;

public static void main(String[] args) {
        displayData(1,2);
    }

Çıktı;

java: reference to displayData is ambiguous
  both method displayData(int...) in varr.Main and method displayData(java.lang.Integer...) in varr.Main match

Hatası alınacaktır. Çünkü displayData(1,2); çağrımı her iki metot için de uygundur.

Uygun çağrım örneği olarak;

    public static void main(String[] args) {
        displayData(new int[] { 1, 2, 3 });
        displayData(new Integer[] { 1, 2, 3 });
    }

Çıktı;

[INT] Number of arguments [values.length]: 3
1 
2 
3 
[INTEGER] Number of arguments [values.length]: 4
4 
5 
6 
7 
5.7.2 Varargs - Object ve Primitive Type Kullanımı

public static void displayData(int... values) ve public static void displayData(Object... values) iki metot tanımlayalım;

public static void displayData(int... values) {
        System.out.println("[INT] Number of arguments [values.length]: " + values.length);
        for (int value : values) {
            System.out.println(value + " ");
        }
    }

    public static void displayData(Object... values) {
        System.out.println("[OBJECT] Number of arguments - [values.length]: " + values.length);
        for (Object value : values) {
            System.out.println(value + " ");
        }
    }

Main metot ile displayData(1, 2, 3) çağrımı yapalım.

    public static void main(String[] args) {
        displayData(1, 2, 3);
    }

derleme anında "ambiguous" hatası alınacaktır.

java: reference to displayData is ambiguous
  both method displayData(int...) in varr.Main and method displayData(java.lang.Object...) in varr.Main match

Eğer metot çağrımı int türünde bir dizi ile yapılmış olsaydı.

    public static void main(String[] args) {
        displayData(new int[]{1, 2, 3});
    }

Çıktı:

[INT] Number of arguments [values.length]: 3
1 
2 
3 

Eğer sadece void displayData(Object... values) metodu tanımlanacak olsaydı.

    public static void main(String[] args) {
        displayData(1, 2, 3);
        displayData(new int[]{3, 4, 5});
    }

    public static void displayData(Object... values) {
        System.out.println("[OBJECT] Number of arguments - [values.length]: " + values.length);
        for (Object value : values) {
            System.out.println(value + " ");
        }
    }

Çıktı:

[OBJECT] Number of arguments - [values.length]: 3
1 
2 
3 
[OBJECT] Number of arguments - [values.length]: 1
[I@3b07d329 

displayData(1, 2, 3); metot çağrımıyla ilgili değerleri sorunsuzca yazdırabilidik.

displayData(new int[]{3, 4, 5}); metot çağrımıyla int türündeki dizi bütünüyle displayData(Object... values) metoduna Object dizisinin ilk elemanı olarak eklenmiş oldu.("Confusing primitive array argument to varargs method")

Bir sonraki örnekte Wrapper Class'da bu karmaşıklığın olmadığı görülecektir.

5.7.3 Vargargs Object ve Wrapper Class Kullanımı

void displayData(Integer... values) ve void displayData(Object... values) iki metot tanımlayalım;

 public static void displayData(Integer... values) {
        System.out.println("[INTEGER] Number of arguments [values.length]: " + values.length);
        for (int value : values) {
            System.out.println(value + " ");
        }
    }

    public static void displayData(Object... values) {
        System.out.println("[OBJECT] Number of arguments - [values.length]: " + values.length);
        for (Object value : values) {
            System.out.println(value + " ");
        }
    }
public static void main(String[] args) {
        displayData(1, 2, 3);
    }

Çıktı:

[INTEGER] Number of arguments [values.length]: 3
1 
2 
3 

Diğer bir senaryo olarak aynı tanımlanmış metotları üç farklı şekilde çağıralım;

    public static void main(String[] args) {
        displayData(1, 2, 3);
        displayData(new Integer[]{3, 4, 5});
        displayData(new int[]{3, 4, 5});
    }

Çıktı:

[INTEGER] Number of arguments [values.length]: 3
1 
2 
3 
[INTEGER] Number of arguments [values.length]: 3
3 
4 
5 
[OBJECT] Number of arguments - [values.length]: 1
[I@7ba4f24f 
  • displayData(1, 2, 3); metot çağrımıyla 1,2,3 değerleri displayData(Integer... values) metodundaki values parametresine Integer[] dizisi içinde eklenmiş oldu.
  • Aynı şekilde displayData(new Integer[]{3, 4, 5}); metot çağrımıyla displayData(Integer... values) ilgili değerler eklenmiş ve çıktıya yazılmış oldu.
  • displayData(new int[]{3, 4, 5}); metot çağrımıyla int türündeki dizi bütünüyle displayData(Object... values) metoduna Object dizisinin ilk elemanı olarak eklenmiş oldu.

Sonuç

Bu yazıda Vararg parametresinin temelde ne olduğunu ve tanımlamala kurallarını öğrenmiş olduk. Kullanım örnekleriyle çeşitli senaryolara değinmiş ve bazı hata durumlarından bahsetmiş olduk.

Kaynaklar:
https://docs.oracle.com/javase/8/docs/technotes/guides/language/varargs.html
https://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html#varargs