Çoğu metot ve yapıcı metot parametre geçilen değerler için kısıtlamalar koyar. Örneğin dizin (index) değerlerinin pozitif tamsayı olması ve nesne referanslarının null
olmaması gibi kısıtlamalarla sıklıkla karşılaşırız. Bu kısıtlamaları metot gövdesinin başında uygulamalı ve açıkça belgelemelisiniz. Bunu yaptığımız taktirde hataların olabildiğince erken saptanması mümkün olabilir, aksi taktirde bunu geciktirmiş oluruz ve bir hata ile karşılaştığımızda nereden kaynaklandığını bulmak zorlaşır.
Bir metoda geçersiz bir parametre verildiğinde, eğer metot kod işletimine başlamadan önce parametrelerin geçerliliğini kontrol ediyorsa erkenden hata üretecek ve mantıklı bir aykırı durum fırlatarak işletimi durduracaktır. Bunu yapmadığı taktirde birkaç farklı durumla karşılaşabiliriz. Birincisi uygulamanız işletimin farklı bir yerinde kafa karıştırıcı bir aykırı durum fırlatabilir. Daha kötüsü, metot işletimini tamamlar ancak yanlış bir sonuç üretebilir. En kötüsü ise bir nesnenin durumunu bozarak ileride tamamen alakasız bir kod parçası işletilirken hataya yol açabilir.
public
ve protected
erişim belirtecine sahip metotlar için Javadoc’un @throws
etiketini kullanarak parametreler geçersiz olduğunda fırlattığınız aykırı durumu belgeleyebilirsiniz. (Madde 74) Genellikle bu aykırı durumlar IllegalArgumentException
, IndexOutOfBoundsException
, veya NullPointerException
olacaktır. (Madde 72) Metot parametrelerine getirdiğiniz kısıtlamaları ve bu kısıtlamalara uyulmadığı taktirde fırlatacağınız aykırı durumları belgeledikten sonra, bunu koda dökmek çok basit olacaktır. İşte bir örnek:
/**
* Metodun yaptığı hesaplamanın detayları..
*
* @param m (modulus), pozitif olmalı
* @return this mod m
* @throws ArithmeticException m sıfıra eşitse veya daha küçükse
*/
public BigInteger mod(BigInteger m) {
if (m.signum() <= 0) {
throw new ArithmeticException("Modulus <= 0: " + m);
}
... // Hesaplama kodu
}
Dikkat ederseniz bu metot m == null
ise NullPointerException
üretecektir ancak biz metodu belgelerken bundan bahsetmedik. Bu aykırı durum metodun içinde bulunduğu BigInteger
sınıfının kendi dokümantasyonunda belgelenmiştir, bu sebeple metot için tekrar yazmaya gerek yoktur. Bu yöntemi kullanarak sınıf içindeki her bir metot için ayrı ayrı NullPointerException
belgelemekten kaçınabilirsiniz.
Java 7 ile dile eklenen Objects.requireNonNull
metodu null kontrolü için esnek ve kolay bir seçenek sunmaktadır. Bu sebeple null
kontrolünü kendiniz yapmanıza gerek yoktur. Bu metot aykırı durum için istediğiniz bir mesajı geçmenize izin verir ve geçtiğiniz parametreyi geri döndürdüğü için değer ataması yaparken de kullanabilirsiniz:
// Java'da null denetimi için örnek kullanım
this.strategy = Objects.requireNonNull(strategy, "strategy");
Tabii ki isterseniz bu geri dönüş değerini yok sayabilirsiniz ve sadece null
kontrolü için kullanabilirsiniz.
Java 9’la birlikte java.util.Objects
sınıfına checkFromIndexSize
, checkFromToIndex
ve checkIndex
gibi aralık kontrolü (range checking) yapan metotlar da eklenmiştir. Bunlar her ne kadar requireNonNull
kadar kullanışlı ve esnek olmasa da ihtiyacınızı karşıladığı taktirde kullanabilirsiniz.
Dışarıya açık olmayan metotlar için (private ve package-private), istemcinin kontrolü tamamen elinizde olacaktır. Bu metotların parametrelerini denetlemek için assert
anahtar kelimesini kullanabilirsiniz.
private static void sort(long a[], int offset, int length) {
assert a != null;
assert offset >= 0 && offset <= a.length;
assert length >= 0 && length <= a.length - offset;
... // Metot gövdesi
}
Esas itibarıyla bu assert
ifadeleri, belirtilen koşulun doğru (true) olması gerektiğini simgelerler ve istemcilerin paketinizi nasıl kullandığından etkilenmezler. Normal geçerlilik kontrollerinin aksine, assert
ifadeleri eğer true
üretmezlerse AssertionError
hatası fırlatırlar. Assert ifadeleri bilinçli olarak etkinleştirilmedikleri sürece devre dışı olurlar, aktifleştirmek için java
komutuna -ea
(veya -enableassertions
) parametresini geçmek gerekir. Bu ifadelerle ilgili daha fazla bilgi için buraya bakabilirsiniz.
Eğer bir metot geçilen parametreleri kendisi kullanmıyor ancak daha sonra kullanılmak üzere bir alanda saklıyorsa, bu parametrelerin geçerliliğini denetlemek daha büyük bir öneme sahip olmaktadır. Örneğin Madde 20‘de yazdığımız, parametre olarak bir int
dizisi alıp bu dizinin List
görünümünü döndüren intArrayAsList
isimli statik fabrika metodunu hatırlayalım. Bu metoda istemci null
geçerse, Objects.requireNonNull
denetiminden dolayı NullPointerException
fırlatılacaktır. Bu denetimin koddan çıkartıldığını varsayarsak, metod bir List
döndürecektir ancak istemci bunu kullanmaya çalıştığı anda NullPointerException
hatası alacaktır. Ancak istemcinin kullanım anında bu List
nesnesinin kaynağını anlamak kolay olmayabilir ve bu hatanın çözülmesini güçleştirebilir.
Bazı durumlarda ise bir hesaplama yapmak için kullanacağımız parametrelerin geçerliliğini önceden kontrol etmek çok da mantıklı olmayabilir. Buna örnek olarak, kendisine verilen nesneleri sıralayan Collections.sort(List)
metodunu verebiliriz. Sıralamanın yapılabilmesi için verilen listedeki nesnelerin birbiriyle karşılaştırılabilir olması gerekmektedir. Eğer sıralama esnasında buna aykırı bir durum bulunursa ClassCastException
hatası üretilecektir ve aslında sort
metodunun yapması gereken de budur. Dolayısıyla, listedeki elemanları birbirleriyle karşılaştırılabilir olup olmadığını önceden kontrol etmek bize çok bir fayda sağlamayacaktır.
Bu maddeden metot parametrelerine zorunlu olmayan kısıtlamalar koymanın iyi bir şey olduğu sonucunu çıkartmayın. Tam tersine, metotlarınız geçilen parametre değerleriyle mantıklı bir işlem yapabildiği sürece kısıtlamalardan kaçınmaya çalışın ki daha geniş bir kullanım alanı bulabilsin.
Özetle, bir metot veya yapıcı metot yazarken parametreler üzerinde ne gibi kısıtlamalar olması gerektiğini iyice düşünün. Bu kısıtlamaları belgeleyin ve metodun hemen başında gerekli denetimleri uygulayın. Bunu bir alışkanlık haline getirmek önemlidir. Sarf edeceğiniz bu küçük çabanın karşılığını geçerlilik denetimleri hata bulduğu zaman fazlasıyla alacaksınız.
[…] Dikkat ederseniz parametrelerin geçerliliğini kopyaları yarattıktan sonra test ettik (Madde 49). Bu garip görünse de aslında gereklidir. Aksi durumda parametreler test edildikten hemen sonra […]