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 eklendidisplayStringValues("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.
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");
iledisplayData(String... values)
metodu çağrıldı.displayData(1, 2);
iledisplayData(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 iseint... 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çin
displayData(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 olanint... 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 olanint... 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);
vedisplayData(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ğerleridisplayData(Integer... values)
metodundakivalues
parametresine Integer[] dizisi içinde eklenmiş oldu.- Aynı şekilde
displayData(new Integer[]{3, 4, 5});
metot çağrımıyladisplayData(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üyledisplayData(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