Effective Java Madde 25: Bir Kaynak Dosyada Gömülü Sınıflar Hariç Tek Bir Sınıf Tanımlayın

Her ne kadar Java derleyicisi sizlere tek bir kaynak dosyada birden fazla gömülü olmayan sınıf (top-level class) tanımlamaya izin verse de, bunu yapmanın bir faydası yoktur hatta ciddi riskleri vardır. Buradaki risk, bir sınıfın farklı yerlerde farklı şekillerde tanımlanabilir olmasıdır. Bu durumda hangi tanımın kullanılacağı, kaynak dosyaların hangi sırada derleyiciye geçildiği ile alakalıdır.

Bunu somutlaştırmak için, aşağıdaki gibi Utensil ve Dessert sınıflarını kullanan bir Main sınıfımız olduğunu farzedelim:

public class Main {
    public static void main(String[] args) {
        System.out.println(Utensil.NAME + Dessert.NAME);
    }
}

Şimdi de hem Utensil hem de Dessert sınıflarının, Utensil.java adında tek bir kaynak dosyada tanımlandığını düşünelim:

// Bir dosyada tanımlanan iki sınıf. Asla yapmayın!
class Utensil {
    static final String NAME = "pan";
}
class Dessert {
    static final String NAME = "cake";
}

Bu durumda main programı tabii ki de pancake yazdıracaktır.

Şimdi de yanlışlıkla Dessert.java adında başka bir kaynak dosya oluşturup iki aynı sınıfı tekrar tanımladığımızı düşünelim:

// Bir dosyada tanımlanan iki sınıf. Asla yapmayın!
class Utensil {
    static final String NAME = "pot";
}
class Dessert {
    static final String NAME = "pie";
}

Şanslıysanız ve bu programı javac Main.java Dessert.java ile derlerseniz, derleyici hata verecek ve Utensil ile Dessert sınıflarının birden fazla tanımı olduğunu söyleyecektir. Çünkü derleyici ilk başta Main sınıfına bakacak ve Utensil referansını görünce (Utensil referansı println içinde Dessert referansından önce gelmektedir) Utensil.java dosyasını okuyup orada hem Utensil hem de Dessert sınıflarının tanımını bulacaktır. Daha sonra komut satırında Dessert.java olduğu için bu dosyaya gidecek, orada da daha önce bulmuş olduğu Utensil ve Dessert sınıflarını tekrar görecek, bu yüzden de hata verecektir.

Eğer programı javac Main.java veya javac Main.java Utensil.java ile derlerseniz, sanki Dessert.java hiç eklenmemiş gibi pancake yazdıracaktır. Ama javac Dessert.java Main.java ile derlerseniz de bu sefer potpie yazdıracaktır. Yani bu programın davranışı derleyiciye geçilen dosyaların sırasına göre değişmektedir, bu asla kabul edilemez.

Bu problem Utensil ve Dessert sınıflarının farklı dosyalarda tanımlanmasıyla çok kolay çözülebilir. Eğer birden fazla sınıfı aynı dosyaya koymak istiyorsanız, statik üye sınıf kullanmayı tercih edin. (Madde 24) Bu sınıflar birbirlerine hizmet ediyorlarsa, statik üye sınıf kullanmak çoğu zaman daha iyi bir alternatif olacaktır çünkü hem okunabilirliği artıracaktır hem de üye sınıfı private tanımlayarak erişilebilirliği azaltma imkanı bulunmaktadır. (Madde 15) Statik üye sınıflar kullanarak yukarıda örneği şöyle değiştirebiliriz:

// Statik üye sınıflar kullanarak yazılan çözüm
public class Test {
    public static void main(String[] args) {
        System.out.println(Utensil.NAME + Dessert.NAME);
    }

    private static class Utensil {
        static final String NAME = "pan";
    }
    
    private static class Dessert {
        static final String NAME = "cake";
    }
}

Buradaki ders çok açık: asla birden fazla sınıf veya arayüz tanımını aynı dosyaya koymayın (gömülü sınıflar hariç). Bu şekilde, derleme anında bir sınıfın birden fazla tanımı olması ihtimalini sıfıra indirmiş olursunuz. Bu da, programınızın davranışının derleyiciye geçilen dosyaların sırasından bağımsız olacağını garanti eder.

Share

Bir Cevap Yazın