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.