Aşağıdakine benzer metotlar sıklıkla karşımıza çıkmaktadır:
// Boş koleksiyonu simgelemek için null döndürüyor, yanlış!
private final List<Cheese> cheesesInStock = ...;
/**
* @return markette bulunan bütün peynirleri içeren bir liste döndürür
* eğer peynir kalmadıysa null döndürür.
*/
public List<Cheese> getCheeses() {
return cheesesInStock.isEmpty() ? null :
new ArrayList<(cheesesInStock);
}
Yukarıdaki kodda peynir listesinin boş olduğu durumu özel bir durum gibi ele alıyoruz. Bu son derece gereksizdir. Bu, istemciyi de null
denetimi yapmaya mecbur bırakacaktır:
List<Cheese> cheeses = shop.getCheeses();
if (cheeses != null && cheeses.contains(Cheese.STILTON)) {
System.out.println("Jolly good, just the thing.");
}
Boş bir koleksiyon veya dizi yerine null
döndüren bütün metotlar bu soruna yol açar. Hata yapmaya çok elverişlidir çünkü istemci null
kontrolü yapmayı unutabilir. Çağrılan metot çoğu durumda bir veya daha fazla eleman döndürüyorsa böyle hatalar yıllar boyunca farkedilmeyebilir. Ayrıca, bir metotta boş koleksiyon yerine null
döndürmek daha zahmetli bir iştir.
Bazen boş koleksiyon döndürmenin null
döndürmeye kıyasla performans kaybına sebebiyet vereceği düşünülebilir çünkü boş da olsa bir koleksiyon yaratmanın bir maliyeti vardır. Bu yaklaşımın iki problemi vardır. Birincisi, eğer performans kaybını ölçümlerle destekleyemiyorsanız bu seviyelerde endişelenmenize gerek yoktur. (Madde 67) İkincisi de bellekte ek yer işgal etmeden boş koleksiyonlar döndürmek mümkündür. Çoğu durumda yapmanız gereken tek şey budur:
// Boş koleksiyon döndürmenin doğru yolu
public List<Cheese> getCheeses() {
return new ArrayList<>(cheesesInStock);
}
Eğer boş koleksiyon üretmenin maliyeti gerçekten uygulamanızın performansını etkiliyorsa, değiştirilemez (immutable) bir boş koleksiyonu tekrar tekrar kullanabilirsiniz çünkü değiştirilemez nesneler serbest bir biçimde paylaşılabilir. (Madde 17) Aşağıdaki kod Collections.emptyList
metodunu kullanarak bunu yapmaktadır. Küme (set) döndürüyor olsaydınız Collections.emptySet
, map döndürüyor olsaydınız da Collections.emptyMap
kullanabilirsiniz. Ancak bunun küçük bir iyileştirmeden ibaret olduğunu unutmayın, eğer farkı ölçebiliyorsanız kullanın:
// İyileştirme - boş koleksiyon yaratılmasını engeller
public List<Cheese> getCheeses() {
return cheesesInStock.isEmpty() ? Collections.emptyList()
: new ArrayList<>(cheesesInStock);
}
Dizilerdeki durum da koleksiyonlarla aynıdır. Uzunluğu sıfır olan dizi yerine null
değeri döndürmeyin. Eğer dizinin elemanı yoksa uzunluk değeri sıfır olmalıdır, bu son derece geçerli bir kullanımdır. Aşağıdaki örnekte sıfır boyutlu bir diziyi toArray
metoduna geçiyoruz. Bu aslında toArray
metodundan istediğimiz dönüş türünü bildirmektedir, yani Cheese[]
:
// Boş olması muhtemel bir dizi döndürmenin doğru yolu
public Cheese[] getCheeses() {
return cheesesInStock.toArray(new Cheese[0]);
}
Eğer boş dizi yaratmanın performansı etkilediğine inanıyorsanız, aynı boş diziyi tekrar tekrar döndürebilirsiniz çünkü boş diziler de değiştirilemez nesnelerdir:
// İyileştirme - boş dizi yaratılmasını engeller
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
public Cheese[] getCheeses() {
return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}
İyileştirilmiş versiyonda cheesesInStock
listesinde eleman olsun veya olmasın aynı boş diziyi toArray
metoduna geçiyoruz. Eğer cheesesInStock
listesi boş ise o zaman geçtiğimiz boş dizi metottan döndürülecektir. Burada performansı iyileştireyim düşüncesiyle toArray
metoduna geçilen diziyi önceden yaratmayı denemeyin, bu tam tersine performansı olumsuz etkileyecektir:
// Bunu yapmayın - diziyi önceden yaratmak performansı kötü etkiler
return cheesesInStock.toArray(new Cheese[cheesesInStock.size()]);
Özetle, boş koleksiyon veya dizi yerine asla null
döndürmeyin. Bu API’ınızın kullanımını hem zorlaştırır hem de hata ihtimalini artırır. Performans katkısı da sağlamaz.