Yoksa sonuna mı geldim? Olurmu hiç öyle şey canım, daha dünya kadar
yolum var. Kaynaklarımdan Java dilini öğrenmeye çalışırken, final anahtar
sözcüğünden bahseden yerlere rastladığımda, java dilini daha başlarda bitirmek
isteyeceklerini düşünmemiştim zaten. Çoğu kaynakta araya sıkıştırılmıştı bu
kavram ancak sağladıkları ile bence öğrenilmesi gereken niteliklerden birisiydi.
Herşeyden önce mükemmelliğin ayrıntılarda gizli
olduğunu biliyordum. Gerçektende çoğu kaynak final kavramına sadece yorum
satırları ile değinmiş olsada, içerik açısından ayrıca incelenmeyi
gerektiriyordu. Aslında buna değerdide. Söz gelimi, geçen hafta üzerinde
çalıştığım kalıtım konusuna kattığı bir özellik vardı. Eğer kaynaklarımda bu
kavrama rastlamamış olsaydım, bu kabiliyeti bilemiyecek ve karşılaştığımda bu
nedir diyerek sağa sola bakacaktım.
O nedenle bu hafta boyunca, final anahtar
sözcüğünün Java diline sağladıkların incelemeye çalıştım. Herşeyden önce işe,
kalıtım ile başlamam gerektiğini düşünüyordum. final anahtar kelimesinin
kullanılabildiği alanlardan bir tanesi metodlardı. Ancak sadece metodlara değil, değişkenlere, sınıf örneklerine ve
sınıflarada uygulanabiliyordu. Bunlarla birlikte kalıtım söz konusu olduğunda final metodlardan bahsetmeden
geçilemiyeceğini bu haftaki çalışmalarımdan anlamıştım. İşe öncelike final
kelimesinin, metodlara kattığı anlamı tanımlamakla başlamak gerekir diye
düşünüyorum.
Bir final metod, Temel sınıfta uygulandığında,
türetilen sınıflarda iptal edilemeyen metodları belirtir. Yani override işleminden bahsetmek
mümkün değildir. Bu etkiyi ispat etmek için her zaman olduğu gibi kahvemden bir
yudum aldım ve bir çırpıda aşağıdaki kodları yazıverdim. Tek amacım, final
metodların türeyen sınıflarda yeniden yazılıp geçersiz kılınıp
kılınamıyacağıydı.
class Temel
{
final void Yaz()
{
System.out.println("Temel sınıf Yaz
Metodu");
}
}
class Tureyen extends Temel
{
public void Yaz()
{
System.out.println("Tureyen sınıf Yaz
Metodu");
}
}
public class FinalDeneme
{
public static void main()
{
Tureyen t=new Tureyen();
t.Yaz();
}
} |
Uygulamayı derlediğimde, aşağıdaki derleme zamanı
hata mesajı ile karşılaştım.

Her şey sonderece açıktı. Gerçektende temel
sınıflarda final olarak tanımlanan metodlar, türeyen sınıflarda override
edilemiyorlardı. Final anahtar kelimesinin kabiliyetleri sadece metodlara
yaptırdıkları ile sınırlı değildi. Örneğin final alanlar vardı. Değişkenleri
final olarak bildirdiğimizde, onlara ilk değerlerin atanmasından sonra,
değerlerini değiştiremiyorduk. Bu aslında C# dilinde constant değişkenlerden
farklı değildi. Yani uygulamada değeri bir kere atandıktan sonra değişmesini
istemediğimiz değişkenler için kullanıyorduk.
Final olarak belirlenen değişkenler ile ilgili
ilginç olan nokta, değer atamalarının çalışma zamanında (runtime) veya derleme zamanında
(debug time)
yapılabilmesiydi. Yani istersem, final bir değişkene ilk değer atamasını çalışma
zamanında gerçekleştirebilirdim. Ancak çalışma zamanına kadar değeri belli
olmayan bir değişken nasıl olabilirdi diyede düşünmeye başlamıştım. Tam o sırada
imdadıma Math sınıfının rastgele sayılar üretmekte kullanılan, random metodu
yetişti. Bu metod çalışma zamanında, rastgele sayılar üretebilirdi. Dolayısıyla
rastgele üretilen bir değeri, programın çalışması boyunca sabit bir değer olarak
saklamak için final anahtar kelimesi kullanılabilirdim. Bu bilgiler ışığında,
konuyu daha iyi kavrayabilmek için, hemen basit bir örnek geliştirmeye karar
verdim.
public class FinalAlanlar
{
final static double deger2;
public static void main(String[] args)
{
final int deger1=7;
deger2=0.125;
final int
deger3=(int)(Math.random()*10);
Yaz(deger1,deger2,deger3);
deger2=100;
deger1=8;
deger3=(int)(Math.random()*5);
Yaz(deger1,deger2,deger3);
}
static void Yaz(int a,double b,int c)
{
System.out.println("Deger1="+a);
System.out.println("Deger2="+b);
System.out.println("Deger3="+c);
}
} |
Uygulamada yapmak istediğim, final olarak
belirlenmiş alanların değerlerini değiştirip değiştiremiyeceğimdi. Bu haliyle
çalışan uygulama bana 4 adet derleme zamanı hatası verdi.

İlk göze çarpan, deger2, deger1 ve deger3
değişkenlerine yeni değerlerin final değişkenler oldukları için
atanamıyacağıydı. Diğer taraftan dikkatimi çeken bir başka nokta, ilk değer
ataması yapmadığım deger2 değişkeni içinde, sonradan yaptığım ilk değer
atamasında hata almamdı. Buradan, final olarak tanımlanan değişkenlere
tanımlandıkları anda değer atanması gerektiği sonucuna varıyordum. Ama gerçekten
durum böylemiydi? Öncelikle koddaki hataların sayısın indirgemem gerektiğini
düşünerek harekete geçtim. Bir yudum sıcak kahve ve ardından aşağıdaki kodları
uygulamadan çıkardım.
deger2=100;
deger1=8;
deger3=(int)(Math.random()*5);
Yaz(deger1,deger2,deger3); |
Şimdi uygulamayı yeniden derlediğimde, hata
sayısının bire düştüğünü ama deger2 için yapılan atamada halen hata aldığımı
gördüm.

O halde yapılacak tek bir şey vardı, deger2 nin
değerini tanımlandığı satırda belirlemek. Kodları son olarak aşağıdaki hale
getirdiğimde herhangibir hata mesajı almadan çalıştığını gördüm. Gerçektende
final olarak belirlenmiş alanlar programın çalışması esnasında
değiştirilemiyorlardı.
public class FinalAlanlar
{
final static double deger2=0.125;
public static void Main(String[] args)
{
final int deger1=7;
// deger2=0.125;
final int
deger3=(int)(Math.random()*10);
Yaz(deger1,deger2,deger3);
}
static void Yaz(int a,int b,int c)
{
System.out.println("Deger1="+a);
System.out.println("Deger2="+b);
System.out.println("Deger3="+c);
}
} |

Diğer taraftan aklıma takılan bir nokta vardı.
Final olarak belirtilmemiş bir alana ilk değer atamadığımızda java derleyicisi
bu değişkenin türüne göre bir ilk değer ataması yapıyordu. Örneğin uygulamada
double tipinden bir değişken tanımlayıp buna ilk değer ataması yapmassak
derleyici bizim için bu değişkene başlangıç değeri olarak 0.0 değerini atıyordu.
Oysa değişkeni final olarak belirttiğimizde, derleyici bizim için varsayılan bir
değer atamsı yapmamakta, üstüne üstelik birde hata mesajı vermekteydi. Bunu
görmek için uygulamaya deger4 isminde double veri türünde bir değişken ekledim. Ancak herhanbiri
değer atamadım.
Daha sonra, bu degeri ekrana yazdırdığımda
derleyicinin 0.0 değerini atadığını gördüm. Ancak, bu değişkeni final olarak
belirttiğimde,
| final static double deger4; |
ve programı derlediğimde aşağıdaki hata mesajını
aldım.

Elde edilen bu hata mesajı bana, final olarak
tanımladığım değişkenlere mutlaka ilk değer ataması yapmam gerektiğini ve hatta
bu atamayı değişkenleri tanımladığım yerde yapmam gerektiğini gösterdi.
Final anahtar kelimesinin değişkenlerce
kullanılması bu şekildeyken, pandora'nın kutusu açılmaya devam ediyor ve içinden
sınıf nesne örnekleri çıkıyordu. Final anahtar kelimesini , nesne
örneklerinede uygulayabilir ve böylece bu nesnelerin programın çalışması boyunca
sadece bir kez yaratılmasını sağlayabilirdik. Bu durumu araştırmak amacıyla aşağıdaki kodları
yazdım. Burada tanımladığım bir sınıfa ait nesneyi final anahtar kelimesi
kullanarak oluşturdum ve daha sonra aynı nesneyi tekrardan oluşturmaya çalıştım.
class Kimlik
{
String ad="Burak";
String soyad="SENYURT";
int id=1000;
public void Yaz()
{
System.out.println(ad+" "+soyad);
}
}
public class FinalAlanlar
{
public static void main(String[] args)
{
final Kimlik personel1=new Kimlik();
personel1.Yaz();
personel1=new Kimlik();
}
} |
Uygulamayı derlediğimde aşağıdaki hata mesajını
aldım.

Sonuç olarak, final olarak oluşturulan bir sınıf
örneği bir kere oluşturulabiliyordu. Diğer yandan final olarak tanımlanan bu
sınıfın üyelerine normal olarak erişilebiliyordu. Final anahtar kelimesinin, bir
sınıf örneğinin sadece bir kere üretilmesini sağlaması benim aklıma başka bir
şey getirmişti. Acaba bir sınıfın yapıcı metodlarına final anahtarını
ekleyebilirmiydim? Nede olsa final anatar kelimesini sınıf metodları ile
birlikte kullanabiliyorduk. Bunun anlamanın yolu denemekten geçiyordu. Önce aşağıdaki
varsayılan yapıcıyı ekleyerek işe başladım.
final public Kimlik()
{
} |
Programı derlediğimde aşağıdaki hata mesajını
aldım.

Belkide, final anahtar sözcüğünün yerinden
kaynaklanıyordur diye düşündüm. Bu sefer final anahtar kelimesini public tanımından sonraya aldım. Ancak
yine aynı hata mesajını elde ettim. Aklıma bu kez parametre alan bir yapıcıda
final anahtarını kullanmak geldi. Nitekim yukarıdaki durum belkide varsayılan
yapıcılara hastı.
final public Kimlik(int YeniID)
{
id=YeniID;
} |
Ancak sonuç yine değişmedi. Demekki final metodlar
tanımlayabiliyor ancak final yapıcılar oluşturamıyordum. Tabi kendi kendime
neden final yapıcılar oluşturmaya çalışayım ki dedim. Biraz anlamsız geldi ama
neleri yapamıyacağımı görmem açısındanda bir yerde iyi oldu. Final anahtar
sözcüğünü metodlara uygulayarak kalıtımda override'ları engellemeyi,
değişkenlere uygulayarak program boyunca değiştirilemeyen değişkenler elde
etmeyi, sınıf nesne örneklerine uygulayarak aynı nesnenin tekrar
türetilememisini sağlamayı, öğrendikten sonra dahada fazlasının olabileceğini hiç
düşünmemiştim.
Meğerse sırada parametreler varmış. Bir metodun
parametrelerini final olarak tanımlayabilir ve bu sayede metodlara gelen
parametre değerlerinin değiştirilmesini engelliyebilirdim. Nasıl mı?
class Hesaplar
{
double UsAl(final double sayi,final double us)
{
return Math.pow(sayi,us);
}
}
public class FinalAlanlar
{
public static void main(String[] args)
{
Hesaplar islem1=new Hesaplar();
System.out.println(islem1.UsAl(2.4,1.25));
}
} |
Bu uygulamada, Math sınıfının pow metodunu
kullanarak, double tipinden bir sayının double değerden üssünü aldım. Önemli
olan kısım UsAl metodunda, parametre değerlerinin final olarak belirlenmesiydi.
Şimdi bu metod içerisinde, gelen parametre değerlerini değiştirmeye
çalışmalıydım.
double UsAl(final double sayi,final double
us)
{
sayi=1.1;
us=3.1;
return Math.pow(sayi,us);
} |
Uygulamayı bu hali ile derleyip çalıştırdığımda,

görülen hata mesajını elde ettim. Demekki, metod
parametreleri, final olarak bildirilirse, metod içinde değerleri
değiştirilemiyordu. Ancak parametreye gönderilen değerlerin verildiği yerlerde
bu durum söz konusu değildi. Bu tip parametreye sahip metodları, değerlerin
kesinlikle içeride değiştirilmemesini istediğimiz durumlarda kullanabilirdim.
Herhalde final ile ilgili pek çok şey hallolmuştu demelimiydim acaba diye
düşünürken, kitaplarımın birisinde final sınıflar isimli bir başlık görüverdim.
Evet sanırım daha herşey bitmemişti. Kahvem bitmişti ancak final anahtar
kelimesinin işlevsellikleri halen daha bitmemişti. Yinede final sınıf kavramı
faydalı ve incelenmeye değer bir kavramdı. Final olarak belirtilen sınıfların en
önemli özelliği, C# dilindeki sealed anahtar sözcüğünün işlevselliğine sahip
olmalarıdır. Yani
final olarak tanımlanmış bir sınıftan başka bir sınıfı kalıtımsal olarak
türetemeyiz.
final class Hesaplar
{
double UsAl(final double sayi,final double us)
{
sayi=1.1;
us=3.1;
return Math.pow(sayi,us);
}
}
class AltSeviye extends Hesaplar
{
} |
Buradaki basit kodlarda, Hesaplar isimli sınıfı
final olarak tanımladım. Sonrada, bu sınıftan kalıtım yolu ile başka bir sınıf
türetmeye çalıştım. Elbette sonuç olarak java derleyicisi beni uyardı ve final
olarak tanımlanmış bir sınıftan başka bir sınıfı türetemiyeceğimi söyledi.

Sınıfların final olarak tanımlanması,
içerdiği metodların veya değişkenlerin final olarak algılanması anlamına
gelmiyordu tabiki. Sadece ve sadece sınfın türetme amacı ile kullanılamıyacağını
belirtiyordu. Final anahtar kelimesi ile ilgili elde ettiğim bilgiler şu an için
bunlarla sınırlıydı. İlerleyen zamanlarda farklı şekillerde kullanımını
görebilirmiyim bilemiyorum. Nitekim bende final olmuş durumdayım. Hem kahvem
bitti hemde pilim. Sanıyorumki bir ara vermenin zamanı geldi. Artık önümüzdeki
hafta boyunca, polimorfizm konusunu incelemek için bana gerekli olan enerjiyi
depolamam gerekiyor.
Burak Selim ŞENYURT
selim@bsenyurt.com