Varargs Java dilinde belirli bir türden sıfır veya daha fazla sayıda argümanı bir metoda tek seferde geçmemizi sağlayan bir mekanizmadır. Variable arity yani değişken sayılı argümanlar ifadesinin kısaltılmasıdır. Arka planda bu mekanizma değişken sayıdaki argümanların toplanıp tek bir dizi içerisinde saklanması ve bu dizinin metoda geçilmesi mantığıyla çalışır.
Örneğin, aşağıda gördüğünüz varargs metot, sıfır veya daha fazla sayıdaki int
değerlerini parametre olarak alıyor ve bunların toplamını döndürüyor.
// varargs metotların basit bir kullanımı
static int sum(int... args) {
int sum = 0;
for (int arg : args) {
sum += arg;
}
return sum;
}
Tahmin edeceğiniz üzere bu metot sum(1, 2, 3)
için 6, sum()
içinse 0 değerini üretecektir. Ancak bazen sıfır veya daha fazla eleman yerine bir veya daha fazla elemanla çalışan metotlar yazmak isteyebilirsiniz. Farzedelim ki argümanlarının içinden değeri minimum olanı bulan bir metot yazmak istiyorsunuz. Varargs kullandığınızda istemci hiç bir argüman geçmeden de bu metodu çağırabileceği için dizinin boyutunu çalışma zamanında kontrol etmek isteyebilirsiniz:
// Bir veya daha fazla argüman geçmek için YANLIŞ varargs kullanımı
static int min(int... args) {
if (args.length == 0) {
throw new IllegalArgumentException("Too few arguments");
}
int min = args[0];
for (int i = 1; i < args.length; i++) {
if (args[i] < min) {
min = args[i];
}
}
return min;
}
Bu çözümün birden fazla problemi var. Birinicisi eğer istemci bu metodu hiçbir argüman kullanmadan çağırırsa, program derleme zamanında değil çalışma zamanında hata verecektir. İkinci problem ise bu kodun çirkin olmasıdır. Dizinin uzunluğunu kontrol etmek zorundayız ve min
değerini başlangıçta Integer.MAX_VALUE
olarak atamadığımız taktirde for-each
döngüsü kullanamayız.
Neyse ki istediğimizi elde etmek için kullanabileceğimiz çok daha iyi bir yol var. Metodu tek bir varargs parametre ile tanımlamak yerine iki parametre kullanabiliriz. Bunlardan ilki kullanmak istediğimiz türde sıradan bir parametre, ikincisi ise yine aynı türde varargs bir parametre olabilir. Buna göre yazılan aşağıdaki kod, az önce anlattığımız bütün problemleri çözmektedir:
// Bir veya daha fazla parametre kabul eden metot yazmanın doğru yolu
static int min(int firstArg, int... remainingArgs) {
int min = firstArg;
for (int arg : remainingArgs) {
if (arg < min) {
min = arg;
}
}
return min;
}
Bu örnekten görüleceği üzere, varargs metotlar değişken sayıda argüman geçebilmek istediğiniz durumlarda oldukça faydalıdır. Bu mekanizma printf
ve yansıma (reflection) için tasarlanıp dile eklenmiştir ve bu ikisi için de büyük kolaylıklar sağlamıştır.
Performans hassasiyetinin yüksek olduğu durumlarda varargs metotları kullanırken daha dikkatli olun. Bir varargs metot her çağrıldığında, arka planda bir dizi oluşturulup içi doldurulur. Deneysel olarak bunu test edip performans kaybını kabul edilemez bulduğunuz taktirde, varargs metot yerine kullanabileceğiniz bir başka yöntem vardır. Diyelim ki metot çağrılarının yüzde 95’inin üç veya daha az argümanla yapıldığını tespit ettiniz. Bu durumda metodun aşırı yüklenmiş beş versiyonunu aşağıdaki gibi tanımlayabilirsiniz:
public void foo() { }
public void foo(int a1) { }
public void foo(int a1, int a2) { }
public void foo(int a1, int a2, int a3) { }
public void foo(int a1, int a2, int a3, int... rest) { }
Artık biliyorsunuz ki metot çağrılarının sadece yüzde 5’i varargs kullanırken ortaya çıkabilecek performans kayıplarından etkilenebilir. Geri kalan yüzde 95 varargs metodu değil, aşırı yüklenmiş ve varargs kullanmayan diğer metotlardan birini çağıracaktır. Bu her ne kadar göze hoş görünmese de, performansın kritik olduğu uygulamalarda hayat kurtarabilir.
EnumSet
metodunun statik fabrikaları, nesne yaratma maliyetini minimuma indirmek için bu tekniği kullanır. Burada kullanılması mantıklı olmuştur çünkü EnumSet
bit alanlarına performanstan ödün vermeden bir alternatif olabilmesi için tasarlanmıştır. (Madde 36)
Özetle, varargs metotlar değişken sayılı argüman geçilebilmesini istediğiniz metotlar için çok faydalıdır. Eğer geçilmesini istediğiniz minimum sayıda argüman varsa, bunları varargs parametrenin önüne aynı tipte normal parametreler olarak ekleyin. Varargs metotların kritik durumlarda performans kaybına yol açabileceğini de unutmayın.