Press "Enter" to skip to content

Effective Java Madde 40: Override Notasyonunu Sürekli Olarak Kullanın

Java kütüphaneleri çok sayıda notasyon (annotation) barındırırlar. Çoğu programcı için bunların en önemlisi @Override notasyonudur. Sadece metot tanımlarında kullanılabilen bu notasyon metodun kalıttığı sınıf veya uyguladığı arayüzdeki başka bir metodu geçersiz kıldığını belirtir. Bu notasyonu düzenli olarak kullandığınız taktirde sizi birçok hatadan koruyacaktır. Şimdi harf ikililerini ifade etmek için yazılmış aşağıdaki Bigram sınıfına bakalım:

// Hatayı bulabilir misiniz?
public class Bigram {
    private final char first;
    private final char second;
    public Bigram(char first, char second) {
        this.first  = first;
        this.second = second;
    }
    public boolean equals(Bigram b) {
        return b.first == first && b.second == second;
    }
    public int hashCode() {
        return 31 * first + second;
    }
    public static void main(String[] args) {
        Set<Bigram> s = new HashSet<>();
        for (int i = 0; i < 10; i++) {
            for (char ch = 'a'; ch <= 'z'; ch++) {
                s.add(new Bigram(ch, ch));
            }
        }
        System.out.println(s.size());
    }
}

Program main metodunda yirmi altı tane harf ikilisini "aa", "bb", "cc" .... "zz" olacak şekilde on kere üst üste bir kümeye (HashSet) eklemekte ve sonra da bu kümenin kaç elemanlı olduğunu yazdırmaktadır. HashSet aynı elemanı en fazla bir kez eklememize izin verdiği için sonucun 26 çıkmasını bekleyebilirsiniz ama 260 çıkacaktır. Peki sorun nerede?

Halbuki sınıfı yazan kişi equals metodunu geçersiz kılmak istemiş (Madde 10) ve hatta hashCode‘u da geçersiz kılması gerektiğini unutmamış (Madde 11). Ancak, equals metodunu geçersiz kılmaya çalışan programcı yanlışlıkla aşırı yükleme (overloading) yapmıştır çünkü Object sınıfından gelen equals parametre türü olarak Object beklerken Bigram sınıfındaki equals metodunda Object yerine Bigram parametresi kullanılmıştır. Bu sebeple de Object sınıfındaki equals metodu Bigram tarafından kalıtılacak ve nesne karşılaştırması için kullanılacaktır. Aynı harf ikililerini ifade eden Bigram nesneleri kalıtılan equals için eşit değildir çünkü farklı nesneleri temsil etmektedirler. Bu sebeple de HashSet içine 26 değil 260 eleman eklenecektir.

Neyse ki bu gibi problemlerin önüne geçmek için çok kolay bir yol var. @Override notasyonunu kullandığımız zaman derleyiciye üst türde tanımlı bir metodu geçersiz kılmak istediğimizi bildirmiş oluyoruz. Bu sayede derleyici bizim için gerekli kontrolleri yapacaktır ve üst türde geçerli kılmaya müsait bir metot yoksa hata verecektir. Şimdi bunu Bigram sınıfındaki equals metodunda nasıl kullanırız onu görelim:

@Override 
public boolean equals(Bigram b) {
    return b.first == first && b.second == second;
}

Bu notasyonu kullandığımızda sınıfı derlemeye çalışırsak aşağıdaki gibi bir hatayla karşılaşırız:

Bigram.java:10: method does not override or implement a method from a supertype
@Override public boolean equals(Bigram b) { 
^

Bu hatayı görünce nerede hata yaptığınızı hemen anlayacaksınız, alnınıza vurup aşağıdaki şekilde düzelteceksiniz:

@Override 
public boolean equals(Object o) { 
    if (!(o instanceof Bigram)) {
        return false;
    }
    Bigram b = (Bigram) o;
    return b.first == first && b.second == second;
}

Bu sebeple, üst sınıflarda tanımlı bir metodu geçersiz kılmak istediğiniz her durumda Override notasyonunu kullanmalısınız.

Modern IDE’lerin hemen hepsi geçersiz kılınan metotlara bu notasyonu otomatik olarak ekleyecek şekilde ayarlanabilir. Hatta Override notasyonunu kullanmayı unuttuğunuz durumlarda sizi eklemeniz için uyarabilirler. Düzenli kullandığınız durumda bu uyarılar sizi istemsiz geçersiz kılma durumlarına karşı da korurlar. Üst türde tanımlı olduğunu bilmediğiniz aynı imzaya sahip bir metot yazacak olursanız, IDE uyarısı sayesinde bunu farkedip durumu düzeltebilirsiniz.

Override notasyonu hem arayüzlerden gelen hem de üst sınıflardan kalıtılan metotları geçersiz kılmak için kullanılabilir. Varsayılan metotların (default method) dile eklenmesi ile arayüzü uygulayan sınıflarda doğru metot imzalarının kullanıldığından emin olmak için bu notasyonun kullanılması yine önerilir. Ancak arayüz sizin kontrolünüzde ise ve varsayılan metot olmadığını biliyorsanız, uygulayan sınıfta arayüz metotlarını geçersiz kılarken Override notasyonunu kullanmamayı seçebilirsiniz.

Soyut sınıflar ve arayüzler yazarken ise bir üst sınıf veya başka bir arayüzden gelen metodları geçersiz kılmak istediğiniz durumlarda Override notasyonunu kullanmak iyi bir fikir olacaktır. Örneğin, Set arayüzü Collection arayüzünü kalıtır ama yeni bir metot eklemez. Bu sebeple Collection arayüzüne yanlışlıkla yeni bir metot eklememek için bütün metot tanımlarında Override notasyonunu kullanması mantıklı olacaktır.

Özetle, Override notasyonu kullanıldığı zaman derleyici sizi türlü hatalardan korur. Bu notasyonu üst türlerdeki metotları geçersiz kılmak istediğiniz durumlarda kullanın. Somut sınıflarda soyut metotları geçersiz kılıyorsanız kullanmanız şart değildir ancak bir zararı da yoktur.

Share

Leave a Reply

%d bloggers like this: