JAVA – Abstract(Soyut) sınıflar ve metodlar
Java programlama dili için soyutlama demek; birilerini soyut tanımlanan her şeyi override etmeye mecbur kılmak demektir dersem sanırım çok da mantıksız olmaz. Biraz karışık mı oldu? O halde devam edelim…. Java’da soyutlama sınıflara ve metodlara uygulanabilmektedir. Aslında buraya kadar söylediğim herşey C# için de aynı. Soyutlama bir konsepttir aslında, dilden dile pek bir farklılık içermez. Soyut tanımlanan hiçbirşey kendi başlarına işe yaramazlar, iş görmezler. Yalnızca bir yol gösterici bir kılavuzdurlar aslında. İçerikleri de yoktur soyut metodların. Soyutlama, kalıtım ile tamamen ilgilidir. Zira az önce bahsettiğim bu yol gösterme ve kılavuzluk türeyen yeni nesnelere yapılmaktadır.
Şimdi biraz daha derinlere dalalım. Kalıtım ve overriding ile ilgili detaylı bir inceleme “Inheritance(Kalıtım) nedir?” başlıklı yazım içerisinde yapmıştım. Soyutlama da kalıtım ve overriding ile çok iç içe olduğu için eksiği olduğunu düşünenler ilk önce burayı tıklayarak gerekli alt yapıyı kurabilirler. Şimdi asıl konumuza geri dönecek olursak, biliyoruz ki ana sınıftan türeyen yavru sınıflar içerisinde ana sınıflara ait metodları override edebiliyorduk. Fakat bu tamamen bizim isteğimize kalmış bir durumdur. Yani siz eğer override etmek isterseniz edersiniz, eğer override etmek istemezseniz ya da buna gerek duymuyorsanız kimse size neden override etmedin diye sormaz. Override etmemeniz derleme zamanında(compile time) ya da çalışma zamanında(run time) herhangi bir hataya da sebebiyet vermez. Pekiala biz eğer bir metodun, metodun içinde bulunduğu sınıftan türeyen tüm alt sınıflarda override edilmesini istiyorsak ne yapmalıyız? İşte soyutlama tam olarak burada karşımıza çıkmaktadır. Yani yazdığımız sınıftan türeyen tüm yavrucularda belirttiğimiz sınıfltarın override edilmesini ve yeniden bir içerik oluşturulmasını zorunlu kılabiliyoruz bir metodu abstract(soyut) tanımlayarak. Bir metodun soyut olarak tanımlanması o metodun bulunduğu sınıftan türeyen tüm sınıflarda override edileceğini garanti altına alır. Peki bu bizim için neden gereklidir? bırakalım da ona yeni sınıfı türeten adam karar versin diyemez miyiz? Diyemeyiz… Şöyle ki; tanımladığımız soyut metod alt üyelerde de mutlaka bulunması gereken fakat ana sınıf için birşey ifade etmeyen bir yapıya sahip olabilir. Yani bu cümleden sonra şu kanıya varabiliriz. Soyut metodlar ana sınıflar için anlamsızdır ve birşey ifade etmezler, asıl anlamlarını ise yavru sınıflar içerisinde kazanırlar. Şimdi bir örnek yaparak bu dediklerimizi biraz daha somutlaştıralım. Sağ taraftaki şekilde örneğe ait UML diagramını bulabilirsiniz.
Main.java | Netbeans Projesini indir AbstractClasses1.rar (12,35 kb) | Gizle | Göster
/** * * @author Cem KEFELI * http://www.cemkefeli.com */ public class Main { static public abstract class Sekil { private String isim; public Sekil() { this.isim = "Ben herhangi bir sekilim."; } public String isimNe() { return this.isim; } static public String Nedir() { return "Bu sinif bir sekil olusturur..."; } public void isimBelirle(String yeniisim) { this.isim = yeniisim; } public void İsimYazdir() { System.out.println(this.isimNe()); } public abstract double alanhesapla(); } static public class Daire extends Sekil { public double yaricap; private double PI = Math.PI; public Daire(double yaricapim) { isimBelirle("Ben bir daireyim"); this.yaricap = yaricapim; } public void SetPi( double Pi ) { this.PI = Pi; } public double GetPi( ) { return this.PI; } @Override public double alanhesapla() { return( this.PI * Math.sqrt(this.yaricap) ); } } static public class Dikdortgen extends Sekil { public double En; public double Boy; public Dikdortgen(double Enim, double Boyum) { isimBelirle("Ben bir dikdortgenim."); this.En = Enim; this.Boy = Boyum; } @Override public double alanhesapla() { return( this.En * this.Boy ); } } public static void main(String[] args) { //Sekil Sekilim = new Sekil(); System.out.println(Sekil.Nedir()); Daire Dairem = new Daire(2.5); Dairem.İsimYazdir(); System.out.println("Yaricap: "+Dairem.yaricap+", PI: "+Dairem.GetPi()+", Alan: "+Dairem.alanhesapla()); Dikdortgen Dikdortgenim = new Dikdortgen(5.0,10.0); Dikdortgenim.İsimYazdir(); System.out.println("En: "+Dikdortgenim.En+", Boy: "+Dikdortgenim.Boy+", Alan: "+Dikdortgenim.alanhesapla()); } }
Program Çıktısı | Gizle | Göster
run: Bu sinif bir sekil olusturur... Ben bir daireyim Yaricap: 2.5, PI: 3.141592653589793, Alan: 4.967294132898051 Ben bir dikdortgenim. En: 5.0, Boy: 10.0, Alan: 50.0 BUILD SUCCESSFUL (total time: 3 seconds)
Soyut metodları ve sınıfları abstract anahtar sözcüğü ile tanımlıyoruz. Yeri gelmişken ve örneği de yapmışken şunu da artık belirmek gerekiyor. Soyut sınıflar, soyut metodları içerisinde bulunduran sınıflardır ve soyut metod bulunduran tüm sınıflar abstract olarak tanımlanmalıdır. Eğer soyut metod bulunduran bir sınıfı abstract olarak tanımlamazsanız hata almanız kaçınılmazdır. Örneği inceleyecek olursak Sekil ve bu ana sınıfıntan türeyen Daire ve Dikdortgen isimli sınıfları göreceksiniz. Sekil sınıfı soyur bir sınıf olarak tanımlanmıştır. Çünkü içerisinde AlanHesapla isminde bir soyut metod bulundurmaktadır. Bu metodun soyut tanımlanmasının nedeni yavru sınıflarda mutlaka override edilerek türetilen şekle özgün alan hesaplayan kod bloğunun oluşturulmasının istenmesidir. Çünkü her şeklin alan hesaplaması farklıdır ve şekle özgüdür. Ayrıca ana sınıf genel bir birleştirici rol görmektedir ve her hangi bir şekil değildir. Dolayısı ile alanının hesaplanması gibi birşey de söz konusu olamaz. Kalıtımın doğal sonucu olarak yavru sınıflarda override edilmemiş IsimYazdir isimli metod da yavru sınılara ait nesne örneklerinden kolaylıkla çağırılabilmektedir. Her şekil için kendisine özgü ismi yazdırmaktadır. Yavru sınıflar içerisinde ise kendilerine özgü hem metodlar hem de alanlar bulunabilmektedir. 75. satırdaki gibi bir ifade hemen dikkatimizi çekmelidir. Çünkü soyut tanımlı bir sınıfa ait nesne örneği oluşturulamaz. Soyut sınıflar mutlaka kalıtım ile kullanılmalıdır. Nesne örneklerinin olmaması nesne tanımlamasının da yapılamayacağı anlamına gelmez. Soyut sınıfa ait bir değişkeni çok rahat bir şekilde yavru sınıfların birisine bağlayabilirsini. Ayrıca nesne örneklerinin olamayacağı yapıcı fonksiyonlarının da olamayacağı anlamına gelmez. Her türlü yapıcı fonksiyonalara sahip olabilirler soyut sınıflar, tıpki 12. satırda olduğu gibi.
Dikkat etmemiz gereken bir diğer nokta ise soyut sınıfların nesne örneği oluşturulamamasının onların static içeriklerine de erişilemeyeceği anlamına gelmeyeceğidir. Soyut sınıfların static metodlarına, soyut sınıfa ait nesne örneği oluşturmadan istediğimiz gibi erişebiliriz. 76. satırda bununla ilgili güzel bir örnek bulunmaktadır. Aklınıza hemen şöyle bir soru gelecektir. Biz soyut sınıflardan normal sınıflar türeterek güzelce kullanabiliyoruz, ya soyut sınıftan yine soyut bir sınıf türetmek istersem ne olur? Güzel soru! Cevap, hiçbirşey olmaz. Soyut sınıflardan soyut sınıflar da türetmek mümkündür. Yeni türeyen soyut sınıf içerisinden ana soyut sınıfa ait soyut metodları yeniden override etmek gibi bir zorunluluk da yoktur. Yeni yavru soyut sınıf içerisinde yeni soyut metodlar tanımlamanız da mümkündür. Ana soyut sınıfa ait soyut metodları yavru soyut sınıf içerisinde override etmeniz de mümkündür. Fakar yavru soyut sınıftan yeni soyut olmayan bir sınıf türettiğiniz zaman hem ana soyut sınıf, hem de yavru soyut sınıfa ait tüm metodlar override edilmek zorundadır.