Final Yapılar

Javadaki bir başka anahtar sözcük olan final, tıpkı static sözcüğü gibi, bir sınıfla, methodla, değişkenle veya bir field ile kullanabilen bir sözcüktür. Bu anahtar sözcük kullanılarak yapılan bir tanım sonradan değiştirilemez. Yani o tanım artık finalize edilmiş bir tanımdır.

Buradaki “tanım” sözcüğü kapsamı geniş şekilde kullanılmıştır. Zira değişken, method ve sınıflarda tanımın finalize olması konuşma dilinde ayrı ifadelere denk gelmektedir.

  • Final değişkenler: Kendisine yeni bir değer atanamaz değişken.
  • Final methodlar: Override edilemez method
  • Final sınıflar: Extend edilemez sınıf

Bu yazımızda ele alacağımız başlıklar için 14 – Final isminde bir proje oluşturalım. Her zaman yaptığımız gibi içerisinde main methodu olan bir TestClass yazacağız. Ancak bu class içerisinde test yapabilmek adına bir senaryo düşünüp bunun java karşılığını kodlamaya ihtiyacımız bulunuyor.

Senaryo olarak bir teknolojik üretim yapan bir şirketi ele alalım. Varsayalım ki bir şirket kendi ürettiği bilgisayarları java objeleri şeklinde kodlayıp, şirketin iç sistemlerinde bu java objeleri ile farklı sistemleri konuşturuyor olsun. Bu şirketin tüm modellerinde (java objeleri), şirket ismi değiştirilemez bir bilgi olarak saklansın. Ancak bunu yaparken kazara yaşanacak değişikliklerin de önüne geçilmek istensin. İlk bakışta bilgisayar markasını, dışarıdan değiştirilemesin diye sınıfın üzerinde private yapıp; aynı şekilde getter ve setterları da private yapmak akla gelebilir. Ancak o sınıfın içinde bu alan ulaşılabilir durumdadır ve sınıf içerisinde getter&setter dışındaki bir method bu alanı güncelleyebilir. Başedilmek istenen bu durumu, geometrik bir şekil olan dairenin java kodunda bulunan pi sayısı gibi düşünebilirsiniz. Pi asla değişmez, tıpkı buradaki markanın sabitliği gibi.

public class Computer {

	public final String brand = "GilmourPC";

}

Yukarıda, şirketin bahsettiğimiz bilgisayarını temsilen, Computer sınıfı paylaşılmıştır. Bu sınıfa baktığımızda field tanımı esnasında bir final sözcüğü görürüz. Bu da brand fieldının değerinin değiştirilemeyeceği anlamına gelir. TestClass içerisinde bu sınıftan yaratılmış bir nesnede bunu gözlemlemek isterseniz, editörden hata alırsınız.

Hatayı gidermek için editörden aldığınız ipucu “Eğer atama yapmak istiyorsanız bu değişkenin önündeki final ifadesini kaldırmanız gerekiyor” şeklindedir.

Peki biz bunu dışarıdan bir parametre alarak nesnenin yaratılma anında atamak isteseydik ne yapmamız gerekirdi? Bu durumu da şirketin ürettiğini varsaydığımız tabletler üzerinden gözlemleyelim. Varsayalım ki şirket her tabletin tekil (unique) bir seri numarası olsun. Bu seri numarası da değiştirilemez olsun. O halde bu alan final olacaktır, ancak, sınıf gövdesinde doğrudan bir değer atanmayacaktır. Aslında bunun da kolay bir çözümü var. O da alanı constructor üzerinden initialize etmektir.

Constructo kullanılmadığında kodumuz hata alınacaktır. Çünkü final değişkenlere veya alanlara, yaratıldığı anda değer ataması yapılmalıdır. İlk örnekte atamayı doğrudan yapmıştık. Ancak burada farklı bir yaklaşım görmekteyiz.

public class Tablet {

	public final String serialNo;

	public Tablet(String serialNo) {
		this.serialNo = serialNo;
	}
	
}

Dikkatinizi çekmesi gereken şey “Final değişkenlere veya alanlara, yaratıldığı anda değer ataması yapılmalıdır” cümlesidir. Buradaki yaratılma anı aslında sınıftan bir instance oluşturulma anıdır. Bu da constructor çağrısı anlamına gelir. Dolayısı ile final alanın constructor üzerinden initialize edilmesi ile yukarıdaki sorunu çözebiliriz. Sonuç olarak bu sınıftan yaratılan objelerin üzerindeki serialNo alanı finalize olur ve nesne yaratıldıktan sonra değiştirilemez.

Bununla beraber bir de değişkenler perspektifinden konuyu incelemek iyi olacaktır. Sınıflardaki alanlar gibi, herhangi bir yerde tanımladığımız değişkenler de final olabilir. Aşağıdaki görüntüde 4 ayrı değişken tanımı görmektesiniz. bunlardan sonuncusunun tanımı final olarak yapılmış durumdadır. Atamaları incelerseniz farklı farklı şekillerde atamanın mümkün olduğunu görürsünüz. Ancak final olan objeye başka bir objeyi atayamamaktayız.

Bu tarz final tanımlanan objelerin üzerindeki fieldlar, final olmadığı sürece değiştirilebilir. Yani bu sınıfın üzerinde bir final olmayan bir renk bilgisi field olarak bulunsaydı, obje final olsa bile alanı güncelleyebilirdik. Ancak final objeye farklı bir objenin ataması yapılamaz. Javada yeni bir obje tanımı yapıldığında o objenin ne olduğu, hafızadaki bir adreste tutulur. Final olarak tanımlanmış nesnelerde bu adresi değiştirmek mümkün değildir. Eşittir operatörü, bu örnekte, secondTablet ve thirdTablet isimli değişkenlerin tuttuğu adresi değiştirmiş ve onların tuttuğu adresi mainTablet’in adresine eşitlemiştir. Son değişkende ise, değişkenin final olması sebebi ile, bu mümkün olamamıştır. Yani değişkenin finalize edilmesi, nesnenin memory üzerindeki adresini tam anlamıyla sabitlemiş, çivilemiştir.

Şimdi bu şirketin, bu ana bilgisayar tanımını biraz genişletelim. Hatta öyle ki ana bilgisayar (Computer sınıfı) sadece yol gösterici bir şablon olsun. Bu sınıfın doğrudan bir nesnesi yaratılamasın. Bu durumda ilk yapacağımız iş sınıfı abstracta çevirmek olacaktır.

Şirket tüm bilgisayarlarına 2 yıl garanti veriyor olsun. Ancak bu garanti süresi de değiştirilemez olsun. Ayrıca şirketin bilgisayar modellerini de konuşalım. Varsayalım ki model yelpazemiz 2’ye ayrılsın.

  • Eğitim modeli: Tek bir modeldir ve alt modelleri yoktur ve olamaz. Model ismi EduModel’dir.
  • Profesyonel model: Alt modelleri olabilir. Model ismi ProModel’dir.

Öncelikle garanti meselesine el atalım. Az önce computer sınıfını abstracta çevireceğimizi yazmıştım. Bu da artık bu sınıftan kendi başına obje yaratamayacağımızı gösteriyor. Ancak abstract sınıfların içerisine bildiğiniz gibi abstract olan veya olmayan methodlar ekleyebiliyoruz. Pratik açısından her ikisinden de birer tane ekleyelim.

public abstract class Computer {

	public final String brand = "GilmourPC";

	final public int getWarrantyDuration() {
		return 2;
	} 
	
	public abstract String getModelName();
	
}

Buradaki getModelName() methodunun yapısına daha evvel değindiğim için tekrar girmeyeceğim. Ancak getWarrantyDuration() methodunda bir farklılık dikkatimizi çekiyor. Method imzasındaki final ifadesi bu methodun override edilemeyeceğini söyler. Yani bu methodun durumu artık finalize olmuştur. Bunu gözlemlemek için alt sınıflara ihtiyaç duyarız. ProModel sınıfımızı bunun için kodlayalım.

public class ProModel extends Computer {

	@Override
	public String getModelName() {
		
		return "ProModel";
		
	}

}

Bu koda baktığımızda standart bir miras işlemi görürüz. Computer sınıfı extend edilmiştir. Ancak bizim test etmek istediğimiz şey ata sınıftaki final methodun override edilip edilemeyeceği konusuydu. Bunu denediğimizde editör bize tıpkı değişken tanımında yaşadığımız sorunun benzerini işaret eder.

Görüleceği üzere final anahtar sözcüğü üst sınıftaki methoddan silinmeden, o methodu override edemeyeceğimize dair bir uyarı alıyoruz. Böylece final ile işaretlenmiş methodların override edilememesi ile, kodumuzun kazayla değiştirilmesinin önüne geçmiş oluyoruz. ProModel sınıfından türetilen tüm sınıflarda da bu bilgi geçerliliğini korur ve method override edilemez.

Son olarak sınıfların nasıl finalize edildiğini gözlemleyelim. Yazının girişinde paylaştığım gibi, final olarak tanımlanan bir sınıf extend edilemez durumdadır. Bu tarz bir ihtiyaç belirli bir nesne modelinin extend edilerek özelleşmesi ve haliyle farklılaşmasının önüne geçmek içindir.

final public class EduModel extends Computer {

	@Override
	public String getModelName() {
		return "EduModel";
	}
	
}

Kodda da görüldüğü gibi final anahtar sözcüğü sınıf deklarasyonunda kullanımaktadır. Şimdi bu sınıf extend edelim ve final sözcüğünün nasıl işlediğini görelim.

Editör bu durumda doğrudan önümüzü keser. Yani hata derleme anındadır. Bu da EduModel’in alt modellerinin oluşmaması için sınıfın final olmasının yeterli olduğu anlamına gelmektedir. Konuyu tersinden yorumlarsak, final bir sınıfın alt sınıfı olamamaktadır.

Bu bölümde final anahtar sözcüğü ile oluşan yapılara değindik Bu anahtar sözcüğün kullanım şekilleriyle ortaya çıkan avantajların iyi anlaşılması, hem uygulamalardaki mimari tasarımlarında hem de kodda güvenlik noktasında faydalı olacaktır.

Leave a Reply

Your email address will not be published. Required fields are marked *