Effective Java Madde 27: Kontrolsüz Uyarılardan (unchecked warning) Kurtulun

Yazıya başlamadan önce Java derleyicisinin verdiği kontrolsüz uyarı (unchecked warning) ne anlama geliyor ondan bahsedelim. Kısaca, derleyici burada bize tür güvenliğini (type safety) garanti edemediğini söylemektedir. Burada ”kontrolsüz” ifadesi biraz yanıltıcıdır çünkü uyarının kontrolsüz olması söz konusu değildir. İfade etmek istediği şey, derleyicinin tür güvenliğini sağlamak için yeterli tür bilgisine sahip olmadığıdır. Bu yüzden de bazı işlemler ”kontrolsüz” gerçekleşecektir.

Üreysel türlerle programlama yaptığınız zaman çeşit çeşit derleyici uyarıları görürsünüz: kontrolsüz tür dönüşümü uyarısı (unchecked cast warning) ve kontrolsüz metot çağrısı uyarısı (unchecked method invocation warning) bunlardan sadece bazılarıdır. Üreysel türlerle olan tecrübeniz arttıkça bu uyarılar azalacaktır.

Bu uyarıların çoğundan kurtulmak kolaydır. Örneğin, yanlışlıkla aşağıdaki gibi bir tanım yaptığınızı düşünün:

Set<Lark> exaltation = new HashSet();

Burada derleyici nazikçe size nerede yanlış yaptığınızı hatırlatacaktır:

Venery.java:4: warning: [unchecked] unchecked conversion
           Set<Lark> exaltation = new HashSet();
                                  ^
     required: Set<Lark>
     found:    HashSet

Bunu görünce, bildirilen hatayı düzeltip uyarıdan kurtulabilirsiniz. Burada eksik olan tür parametresi belirtmek için kullanılan baklava işlecidir (diamond operator). Dikkat ederseniz exaltation zaten parametreli bir tür olarak tanımlandığı için nesne yaratırken tekrar tür parametresi geçmenize gerek yoktur, işlecin kendisini kullanmanız yeterlidir. Java 7’den sonra buradaki tür parametresi otomatik olarak algılanır (bu örnekte Lark).

Set<Lark> exaltation = new HashSet<>();

Bazı uyarıları gidermek ise oldukça zor olabilir. İleriki maddelerde bunların örneklerini göreceğiz. Ancak, derleyiciden böyle bir uyarı aldığınız her durumu değerlendirin ve uyarıdan kurtulmak mümkünse bunu yapın! Eğer bütün uyarıları giderebilirseniz, çalışma zamanında tür güvenliği sağladığınızdan emin olabilirsiniz. Programınız ClassCastException üretmeyecektir ve kötü sürprizlerle karşılaşma olasılığı azalacaktır.

Eğer bir uyarıdan kurtulamıyorsanız ancak bu uyarıya sebep olan kodun tür güvenliğini tehlikeye atmadığından eminseniz o zaman @SuppressWarnings("unchecked") notasyonunu (annotation) kullanarak bu uyarıyı gizleyebilirsiniz. Ancak tür güvenliğinden emin olmadan bunu yaparsanız kendinizi kandırmış olursunuz. Kod uyarı vermeden derlenmiş olur ama çalışma zamanında hala ClassCastException fırlatabilir. Güvenli olduğunu bildiğiniz durumlarda kontrolsüz uyarıları gizlemek yerine sürekli görmezden gelirseniz de bu sefer gerçekten problem çıkaracak yeni bir uyarı verildiğinde bunu farketmezsiniz. O yüzden tür güvenliğini garanti edebildiğiniz durumlarda bu uyarıları gizlemek en doğrusudur.

SuppressWarnings notasyonu bir yerel değişkenden sınıflara kadar birçok yerde tanımlanabilir. Bu notasyonu mümkün olan en dar alanda kullanmak en doğrusudur. Bu da genelde bir değişken tanımlarken veya bir iki satırlık kısa metotlar/yapıcı metotlar için kullanılması demektir. Sınıflar için SuppressWarnings notasyonunu kullanmayın çünkü kritik uyarıları istemeden gizleyebilirsiniz.

Eğer SuppressWarnings notasyonunu uzun bir metot veya yapıcı metot için kullanıyorsanız, bunu yerel bir değişkene taşımanız mümkün olabilir. Bunun için yeni bir yerel değişken tanımlamanız gerekebilir ama bunu sorun etmeyin. Örneğin, ArrayList‘ten gelen toArray metoduna bakalım:

public <T> T[] toArray(T[] a) {
    if (a.length < size) {
        return (T[]) Arrays.copyOf(elements, size, a.getClass()); 
    }
    System.arraycopy(elements, 0, a, 0, size);
    if (a.length > size) {
        a[size] = null;
    } 
    return a;
}

Bu kodu derleyince aşağıdaki gibi bir uyarı alırsınız:

ArrayList.java:305: warning: [unchecked] unchecked cast
return (T[]) Arrays.copyOf(elements, size, a.getClass());
                           ^
     required: T[]
     found:    Object[]

Uyarı return satırından gelmektedir ancak buraya SuppressWarnings notasyonu koymak yasaktır. Bu sebeple notasyonu metoda eklemeyi düşünebilirsiniz ama böyle yapmayın. Bunun yerine bir yerel bir değişken tanımlayıp döndürmek istediğiniz değeri atayın ve bu değişkene notasyonu ekleyin:

// SuppressWarnings etki alanını daraltmak 
// için yerel değişken tanımladık
public <T> T[] toArray(T[] a) {
    if (a.length < size) {
        // Burada tür güvenliği vardır çünkü yarattığımız dizinin türü
        // parametre geçilen tür ile aynıdır: T[]
        @SuppressWarnings("unchecked") 
        T[] result=(T[]) Arrays.copyOf(elements, size, a.getClass());
        return result;
    }
    
    System.arraycopy(elements, 0, a, 0, size);
    if (a.length > size) {
        a[size] = null;
    }
    return a;
}

Bu metot tertemiz derlenecektir ve SuppressWarnings notasyonunun etki alanı sadece result yerel değişkeniyle kısıtlı bırakılmıştır.

@SuppressWarnings("unchecked") notasyonunu kullandığınız her durum için bir yorum ekleyip bunun neden güvenli olduğunu açıklayın. Bu şekilde hem başkalarının kodu anlamasına yardımcı olursunuz hem de ileride kodu değiştiren kişilerin tür güvenliğini bozmaması için bir kılavuz bırakmış olursunuz. Yorum yazarken zorlandığınız hissediyorsanız da iyi düşünün, belki de güvenli zannettiğiniz kod parçası güvenli değildir.

Özetle, kontrolsüz uyarılar (unchecked warnings) önemlidir, bunları görmezden gelmeyin. Bu uyarıların her biri potansiyel bir ClassCastException aykırı durumunu temsil eder. Bunlardan kurtulmak için elinizden geleni yapın. Kurtulamıyorsanız da, güvenli olduğundan emin olduğunuz yerlerde @SuppressWarnings("unchecked") notastonu ile bu uyarıları gizleyin. Bunu yaparken de notasyonun etki alanını minimize edecek şekilde yapın ve mutlaka yorum ekleyerek neden güvenli olduğunu açıklayın.

Share

One Reply to “Effective Java Madde 27: Kontrolsüz Uyarılardan (unchecked warning) Kurtulun”

Bir Cevap Yazın