Previous
Next

JAVA - Interfaces(Arayüzler)

by Cem Kefeli 18. Ekim 2009 10:52

JavaJava'da kalıtımın nasıl gerçekleştirildiğini "Inheritance(Kalıtım) nedir?" başlıklı yazım içerisinde görmüştük. Kalıtım, OOP(Object Oriented Programming) bir dilde olmazsa olmazlardandır ki, uygulama geliştiricilere çok büyük güzellikler sunar. Tüm canlılarda olduğu bigi nesneler arasında da kalıtım ilişkisi kurulur ve yeni yeni türler tanımlanmasını mümkün kılar. Bir nesne tanımlarsınız, sonra bu nesneden başka bir nesne daha türetebilirsiniz. Fakat ya iki ya da daha çok nesnenin bir araya gelip bir nesne oluşturmasını isterseniz ne olacaktır? İşte burada durum karışıktır! Çünkü java çoklu kalıtmı desteklemez. Bir nesneyi yalnızca bir tane nesneden türetebilirsiniz. Bu aşamada imdadımıza Interfaces(Arayüzler) yetişir ve her ne kadar tam anlamıyla çoklu kalıtım olmasa da buna benzer bir davranışı bizlere sunar.

  • Arayüzler içerisinde yalnızca Abstract(Soyut) metodlar ve sabitler(constants) bulundurabilirler, gövdeli metodlar barındıramazlar. Soyut metodları ve sınıfları incelerken de soyut sınıfların bir birleştirici rol oynadıklarını, soyut sınıflardan türeyen sınıflara adete bir kılavuz gibi yol gösterdiğinden bahsetmiştik. Fakat soyut sınıflar gövdeli metodlar yani iş yapabilen metodlar da barındırabiliyorlardı.
  • Ayrıca bir sınıfın birden fazla soyut sınıftan türemesi de söz konusu değildi.
  • Hem soyut sınıflarda hem de arayüzlerde ortak olan bir nokta ise, soyut sınıflarda abstract olarak tanımlanan ve arayüzlerde tanımlanan her metodun bu soyut sınıf ve arayüzlerden türeyen tüm sınıflarca override edilmesi gerekliliğidir. Bu arada şunu söylemek gerekir ki arayüzler içerisindeki metod tanımlamalarında soyut sınıflarda kullandığımız gibi abstract anahtar sözcüğünü kullanamıyoruz. Nedeni ise arayüzlerin içerisindeki tüm metodların zaten abstract olarak değerlendirilmesidir. Dilin mimarisini oluşturanlar ayrıca bir daha abstract tanımlama yapılmasını gerekli görmemişler. Eğer yazarsanız hata mesajı ile karşılaşırsınız.
  • Bir diğer nokta ise arayüz içerisinde yapılan tüm metod tanımlamaları için bir erişim belirleyicisinin kullanılamayacağıdır. Varsayılan erişim belirteci olarak public seçilmiştir ve sizin bunu değiştirme şansınız da yoktur. Eğer bir metodun erişim belirleyicisini değiştirip örneğin private gibi bir erişim belirteci verebilseydiniz türeyen sınıflarda bu metodları override edebilmek imkansız olacaktır. Bu da arayüzler içerisindeki tüm metod tanımlamaları türeyen sınıflarda override edilmelidir ilkesi ile çelişecektir. Tabi ki metodların dönüş bildirim değerlerinin olmayacağı anlamına gelmemektedir ve bu ikisi birbirinden ayrı şeylerdir.Arayüzler içerisinde metodlar haricinde global alanlar da tanımlayabildiğimizi söylemiştik. Erişim belirleyicisi olarak bu alanlarlar da yine public olarak değerlendirmektedir. Bunu da sizin değiştirme şansınız yoktur. Ayrıca alanlara static ve final erişim belirteci de eklenmiştir, metodlardan farklı olarak. Tabiki bunlar arayüzler içerisinde birebir olrak yazan şeyler değildir. Yazılmasa dahi varsayılan olarak kabul görmektedirler.
  • Soyut sınıflardan başka soyut sınıflar türetebiliyorduk. Bu böyle arda arda devam edip gitmekteydi. Yine hatırlarsanız türeyen soyut sınıflarda, türedikleri soyut sınıf içerisinde yer alan soyut metodları override etme gibi bir zorunluluk da yoktu. Aynısı arayüzler için de geçerlidir. Arayüzler de başka arayüzlerden türeyebilmektedir ve bu özellik de arayüzler arasında da kalıtım ilişkisinin kurulabilmesini sağlamaktadır.

Bu birkaç önemli noktanın üzerinde durduktan sonra şimdi biraz daha uygulamaya dönük birşeyler yapabiliriz artık. Uygulama demişken Java'da arayüzler interface anahtar sözcüğü ile tanımlanmakta ve implements anahtar sözcüğü ile de uygulanmaktadır. Aşağıdaki örnekte yukarıda bahsettiğimiz bu birkaç özellik ile ilgili bir uygulama vermekteyim.

Main.java  |  Netbeans Projesini indir Interfaces1.rar (13,83 kb)  |  Gizle  |  Göster
/**
 *
 * @author Cem KEFELI
 * http://www.cemkefeli.com
 */

public class Main
{
    interface IKendiniTanit
    {
        void IsminiYaz();
    }
    interface IKendiniDetayliTanit extends IKendiniTanit
    {
        void NumaraniYaz();
        void SoyadiniYaz();
    }
    interface MaasIslemleri
    {
        double MaasHesapla();
    }
    abstract static class KISI implements IKendiniDetayliTanit, MaasIslemleri
    {
        public String Isim="";
        public String Soyad="";
        public String Numara="";
        public double TabanMaas = 3000.0;
        public double MaasKatsayi=0.0;
        //
        KISI(double MaasKatsayim, String Ismim, String Soyadim, String Numaram)
        {
            this.Isim = Ismim;
            this.Soyad = Soyadim;
            this.Numara = Numaram;
            this.MaasKatsayi = MaasKatsayim;
        }
        public void NumaraniYaz()
        {
            System.out.println(this.Numara);
        }
        public void SoyadiniYaz()
        {
            System.out.println(this.Soyad);
        }
        public void IsminiYaz()
        {
            System.out.println(this.Isim);
        }
        public void TumBilgileriDok()
        {
            System.out.println("isim: "+this.Isim+", soyad: "+this.Soyad+", numara: "+this.Numara);
        }
    }
    static class GenelMudur extends KISI
    {
        GenelMudur(String Ismim, String Soyadim, String Numaram)
        {
            super(2.5, Ismim, Soyadim, Numaram);
        }
        public double MaasHesapla()
        {
            return(this.TabanMaas*this.MaasKatsayi + 2000.0);
        }
    }
    static class Mudur extends KISI
    {
        Mudur(String Ismim, String Soyadim, String Numaram)
        {
            super(1.5, Ismim, Soyadim, Numaram);
        }
        public double MaasHesapla()
        {
            return(this.TabanMaas*this.MaasKatsayi + 1000.0);
        }
    }
    static class Muhendis extends KISI
    {
        Muhendis(String Ismim, String Soyadim, String Numaram)
        {
            super(1.0, Ismim, Soyadim, Numaram);
        }
        public double MaasHesapla()
        {
            return(this.TabanMaas*this.MaasKatsayi + 500.0);
        }
    }
    public static void main(String[] args)
    {
        GenelMudur GenelMudurum = new GenelMudur("Cem","Kefeli","1111111");
        Mudur Mudurum = new Mudur("Onur","Kelebek","2222222");
        Muhendis Muhendisim = new Muhendis("Meltem","Uyanir","5555555");
        GenelMudurum.TumBilgileriDok();
        System.out.println("GenelMudur maasi:"+GenelMudurum.MaasHesapla());
        Mudurum.TumBilgileriDok();
        System.out.println("Mudur maasi:"+Mudurum.MaasHesapla());
        Muhendisim.TumBilgileriDok();
        System.out.println("Muhendis maasi:"+Muhendisim.MaasHesapla());
    }
}	
Program Çıktısı  |  Gizle  |  Göster
run:
isim: Cem, soyad: Kefeli, numara: 1111111
GenelMudur maasi:9500.0
isim: Onur, soyad: Kelebek, numara: 2222222
Mudur maasi:5500.0
isim: Meltem, soyad: Uyanir, numara: 5555555
Muhendis maasi:3500.0
BUILD SUCCESSFUL (total time: 3 seconds)	

Interfaces1 - UMLŞimdi bir taraftan yandaki UML diagramına bakarak yavaş yavaş ve adım adım yazdığım bu örnek programcığı inceleyelim. IKendiniTanit isimli arayüzümüz IsminiYaz isimli bir metod içeriyor ve metodun göndesi de yok. Burada her hangi bir tuhaflık da yok arayüzlerin yalnızca gövdesiz metodlardan ve sabit üyelerden oluşacağını daha önce söylemiştik. IKendiniDetaylıTanit isimli arayüz ise IKendiniTanit isimli arayüzden türüyor ve NumaraniYaz, SoyadiniYaz isimli iki tane de metod içeriyor. Arayüzler arasında kalıtım ilişkisi kurulabir. Yani bir arayüz diğer bir arayüzden türeyebilir. Bunun daha önceleri karşımıza çıkan klasik kalıtımdan hiçbir farkı da yoktur gerek işleyiş gerekse notasyon açısından. Fakat dikkatimizi çekmesi gereken birşey var ki o da ilk başlarda sözlediğimiz 'arayüzlerin uygulandığı sınıflarda, arayüz içerisindeki tüm metodlar override edilmelidir' kavramını bir kez daha gözden geçirmemiz gerektiğidir. Eğer bir arayüz bir başka arayüzden türüyor ise türeyen arayüzde, ana sınıfa ait metodların override edilmesi gerekmemektedir. Çok da mantıklı aslında, çünkü ipucu az önce söylediğimiz cümlede gizli. Arayüzler ancak gerçek sınıflara(Soyut sınıflar hariç) uygulandığında o arayüze ait tüm metodların override edilmesi gibi bir zorunluluk doğmaktadır. Burada yaptığımız yalnızca arayüzün kapsamını biraz daha genişletmektir. Bir diğer arayüz ise MaasIslemleri arayüzüdür ve MaasHesapla isiminde bir de metod içermektedir. Bu son arayüz ile birlikte yapmak istediğimiz arayüz tanımlamalarını bitirmiş olduk. İlerlemeye devam ediyoruz...
 

KISI isimli sınıf abstract(soyut) olarak tanımlanmıştır ve ayrıca da IKendiniDetaylıTanit, MaasIslemleri isimli arayüzlerden oluşmaktadır. Soyut sınıflar da istediğiniz arayüzlerden oluşabilir, sınıfın soyut olması buna engel değildirler. KISI isimli sınıf içerisinde Isim, Soyad, Numara, TabanMaas ve MaasKatsayi isminde 5 adet üye oluşturulmuştur ve KISI sınıfının yapıcı fonksiyonu ile bu üyelere başlangıç değerleri verilmektedir. Burada dikkatimizi çekmesi gereken nokta soyut sınıfların da tıpkı normal sınıflar gibi yapıcı metodlara sahip olabileceğidir. KISI sınıfı içerisinde arayüzler ile gelen NumaraniYaz, SoyadiniYaz ve IsminiYaz isimli metodlar override edilmiştir ve ayrıca da bu soyut sınıf içerisinde TumBilgileriDok isimli yeni bir metod da oluşturulmuştur. Ayrıca bu metod iş yapabilen kod parçacıklarına sahip bir gövdeye de sahiptir. Fakat yüne arayüzler ile alınan MaasHesapla isimli metodun override edilmediğini göreceksiniz. Az önce türeyen arayüzün, türediği arayüze ait metodları override etmesine gerek olmaması ilkesi aynen burada da karşımıza çıkmaktadır. Çünkü soyut sınıflar da bir kılavuzdur ve bu soyut sınıflar da soyut metodlar içerebilmektedirler. Tıpkı soyut sınıftan türeyen bir soyut sınıfın, türediği soyut sınıfa ait metodları override etmesine gerek olması gibi burda da uygulandığı arayüze ait metodları override etme gibi bir zorunluluğu yoktur. Peki biz bu KISI isimli sınıfı neden soyut tanımladık? Bu sorunun cevabını ilerleyen dakikalara bırakıyorum.

Gelelim GenelMudur isimli sınıfa. GenelMudur isimli sınıf KISI isimli sınıftan türemiştir ve nihayetinde arayüzlerden uygulanan MaasHesapla isimli metod bu sınıf içerisinde override edilmiştir. Çünkü artık edilmek zorundadır. Çünkü GenelMudur isimli sınıf artık gerçek bir sınıftır. Ayrıca az önce KISI isimli soyut sınıf içerisinde tanımladığımız yapıcı metodu bu sınıf içerisinden de super anahtar kelimesi ile kolayca çağırabilmekte ve kalıtsal olarak iletildiğini görebilmekteyiz. MaasHesapla isimli metod içeriside yapıcı metodda tanımlanan katsayı ve TabanMaas isimli üye kullanılarak GenelMudur sınıfına özgü maaş hesaplaması yapılabilmektedir. Aynı şekilde bir hesaplama kendi yapıcı metodlarının tanımladığı şekilde Mudur ve Muhendis isimli sınıflar için de aynı şekilde yapılmaktadır. Az önce KISI isimli bir sınıfı neden tanımladığımız sorusunu yarım bırakmıştık. Sanırım artık cevaplama zamanı geldi. Soyut sınıları anlatan yazımı okuyacak olursanız soyut sınıflardan birer nesne örneği oluşturulamayacağını görmüş olacaksınız. Bizim buradaki temel amacımız da bu yöndedir. Çünkü KISI sınıfından bir nesne örneği oluşrmamıza gerek yoktur. KISI yalnızca kendisinden türeyen sınıflara rehberlik edecek soyut bir sınıftır. Gerçek anlamda asıl kişiler, GenelMudur, Mudur ve Muhendis sınıflarından oluşturulan nesne örnekleridir.

Program çıktısını inceleyecek olursanız her bir nesne örneğimize başlangıç değerlerini verdiğimizi ve her bir nesne örneği için de farklı maaş değerlerinin hesaplandığını göreceksiniz, hepsinin aynı arayüzden ve aynı soyut sınıftan türemiş olmalarına rağmen. İşte bu overriding kavramının bizlere sunmuş olduğu bir güzelliktir.

Bir sınıf ile bu sınıftan türeyen bir başka sınıf arasında "is a" adı verilen bir bağımlık vardır. Yani yukarıdaki örnekten bahsedecek olursak. Mudur bir KISI'dir diyebiliriz. Fakat arayüzlerin kalıtımı arasında böyle bir ilişki bulunmayabilir. İki ya da daha çok arayüzden türeyen arayüzlerin türedikleri arayüzler içerisindeki metod tanımlamalarına çok dikkat edilmelidir. Eğer aynı isimli ve imzaları da aynı olan metodlar varsa bu soruna yol açacaktır. Unutmamak gerekir ki. metodlar için dönüş tipleri bir ayırt edici özellik değildir. Bir arayüz bir başka bir arayüzün içerisinde de tanımlanabilmektedir. Hatta bir arayüz bir sınıfın içinde dahi tanımlanabilmektedir. Aşağıda bununla ilgili bir örnek veriyorum.

Main.java  |  Gizle  |  Göster
/**
 *
 * @author Cem KEFELI
 * http://www.cemkefeli.com
 */

public class Main
{
    interface IBaseInterface
    {
        void BaseInterface();
        interface IChildInterface1
        {
            void ChildInterface1();
        }
        interface IChildInterface2
        {
            void ChildInterface2();
        }
    }
    interface IInterface extends IBaseInterface, IBaseInterface.IChildInterface1
    {

    }
    public static class Class1 implements IBaseInterface.IChildInterface1
    {
        public void ChildInterface1()
        {
        }
    }
    public static class Class2 extends Class1 implements IBaseInterface.IChildInterface2
    {
        public void ChildInterface2()
        {
        }
    }
    public static class Class3 implements IBaseInterface
    {
        public void BaseInterface()
        {
        }
    }
    public static class Class3_1 implements IBaseInterface
    {
        public void BaseInterface()
        {
        }
        public void ChildInterface2()
        {
        }        
    }
    public static class Class4 implements IInterface
    {
        public void BaseInterface()
        {
        }
        public void ChildInterface1()
        {
        }
    }
    public static class Class5 implements IBaseInterface, IBaseInterface.IChildInterface1,
IBaseInterface.IChildInterface2
    {
        public void BaseInterface()
        {
        }
        public void ChildInterface1()
        {
        }
        public void ChildInterface2()
        {
        }
    }
    public static void main(String[] args)
    {
        
    }
}

Bu örnek aslında iş yapabilen kod parçacıklarından oluşmuyor yalnızca yukarıda söz ettiğimiz birkaç özelliğin nasıl oluyor da gerçek olabildiğini inceleyeceğiz. İlk dikkatimizi çekmesi gereken, arayüz tanımlamaları da dahil, Main isimli bir sınıfın içerisinde yapılıyor olması. Buradan çıkarmamız gereken sonuç arayüzlerin sınıfların içerisinde tanımlanabileceğidir. İkincisi ise IBaseInterface arayüzünün içerisinde IChildInterface1 ve IChildInterface2 tanımlanmasıdır. Bir arayüzün içerisinde bir başka arayüz de tanımlanabilir. Bu şekilde tanımlanan arayüzlere Nested Interface yani dahili arayüzler ismi verilmektedir. Asıl dikkatimizi çekmesi gereken ise 21. satırdır. Çünkü burada bir arayüz birden fazla arayüzden türetilmiştir. Oysa ki şimdiye kadar hep Java'nın çoklu kalıtımı desteklemediğini söylemiştik. Java arayüzler arasında çoklu kalıtımı desteklemektedir. Buraya kadar hep farklı farklı tanımlamalar yaptık durduk. Peki ya bu arayüz tanımlamalarını farklı farklı şekillerde sınıflara ugularsak ne olur?

  • Sınıflara bir arayüzün alt arayüzü(yani nested interface) uygulanabilmektedir. Class1 sınıfına IBaseInterface.IChildInterface1 arayüzü uygulanmaktadır. Bu yüzden Class1'in yalnızca IBaseInterface.IChildInterface1 içerisinde yer alan ChildInterface1 metodunu override etmesi yeterlidir.
  • 31. satırda yine farklı bir uygulama ile karşı karşıyayız. Çünkü bir sınıfa hem arayüzler ugulanıyor hem de aynı sınıf bir başka sınıftan türüyor. Sınıflara aynı anda arayüzler uygulanırken, hem de bir başka sınıftan türeyebilmektedir. Class2 buna bir örnektir ve yalnızca sınıfa uygulanan arayüz içerisindeki metodların override edilmesi yeterli olacaktır. Bununla beraber Class2'nin türetildiği Class1 içerisindeki metodların override edilmesi de mümkündür.
  • Bir sınıfa, içerisinde başka arayüzler de bulunan(ana arayüz) bir arayüz uygulanabilmektedir. Class3 buna güzel bir örnektir. Fakat burada bize ilk bakışta tuhaf gelebilecek bir durum söz konusudur. Çünkü IBaseInterface arayüzü içerisindeki IChildInterface1, IChildInterface2 arayüzlerindeki metodlar Class3 içerisinde override edilmemiştir ve bu bir hata da değildir. Nested bir interface'in uygulandığı sınıflarda en üst seviyeden arayüzün içerisindeki metodların override edilmesi yeterlidir. Bununla beraber eğer Class3_1'e bakacak olursanız ChildInterface2 isimli metodun da override edilebildiğini ve bir hata ile de karşılaşılmadığını göreceksiniz. Fakat ChildInterface2 metodunun override edilmesi bir zorunluluk değildir.
  • Class4'e ise başka arayüzlerden türeyen IInterface isimli arayüz uygulanmaktadır. Class4 içerisinde IInterface'in türediği arayüzler içeriside bulunan BaseInterface ve ChildInterface1 metodları mutlaka override edilmelidir.
  • Az önce Class3'ü incelerken dahili arayüz uygulanan bir sınıfta yalnızca en üst seviyedeki arayüz içerisindeki metodların override edilmesinin yeterli olacağından bahsetmiştik. Fakat biz alt arayüzlere ait metodları da override edersek bir sorunla karşılaşmıyorduk. Bu örneğimiz ile Class5 içerisinde IBaseInterface arayüzünün alt arayüzleri olan IBaseInterface.IChildInterface1 ve IBaseInterface.IChildInterface2 içerisinde yer alan ChildInterface1 ve ChildInterface2 metodlarının da override edilmesini zorunlu hale getirmiş bulunmaktayız.

Yorumlar (2) -

yılmaz
yılmaz Turkey
17.04.2013 07:19:59 #

güzel yazı olmuş elinize sağlık..arayüzlerin nasıl kullanıldığını iyi açıklamışsınız ama niçin kullanıldığı hiçbir yerde açıklanmıyor nedense..arayüzlerin metotlarının içi boşsa sadece metot adlarından oluşuyorsa neye yarar? ben arayüz implement etmeden de kişi isimli bir sınıf oluşturabilirim ve metotları bu sınıfta tanımlarım ve diğer normal sınıfları da bundan kalıtım edebilirim değil mi??o halde arayüzlere ne gerek var?

Yanıtla

admin
admin Turkey
26.04.2013 07:54:43 #

Selam Yılmaz,
Aslında yazının başında vurgulamaya çalışmıştım fakat tekrar üzerinden geçiyorum.

Java da coklu kalitim desteklenmiyor. Yani biz javada bir nesne hem x nesnesinden hem de y nesnesinden turesin diyemiyoruz. Bunun bazi nedenleri var. Yazinin basinda bu konudan bahsettim. En genel anlamda coklu kalitimi bir nebze olsun saglayabilmek amaciyla interface ler var. Cunku bir nesne birden fazla interface den implemente olabiliyor. Diceksin ki olmasa ne olurdu. Bu tamamen dilin mimarisi ile ilgili. Sundugu esneklikler ile ilgili.

Umarim faydali olmustur,

Tesekurler,
Cem

Yanıtla

Yorum ekle

biuquote
  • Yorum
  • Canlı önizleme
Loading

Hakkımda...

Cem KEFELİ

Electronics and
Telecommunication Eng.
devamı...


Son yapılan yorumlar...

Comment RSS

Yasal bir uyarı...

Disclaimer"Bu web sitesinde görmüş olduğunuz bilgilerin, dokümanların ve diğer materyallerin kullanılmasından doğabilecek hiç bir sorumluluktan site sahibi sorumlu tutulamaz. Web sitesi içerisinde yer alan yazılar, yorumlar, resimler ve diğer tüm içerikler yalnızca sahibinin görüşünü yansıtmakta olup içeriğin sahibi kişilerin çalıştığı kurumları bağlayıcı hiç bir nitelik taşımamaktadır. Yapılan tüm alıntılar mutlaka kaynak gösterilerek verilmeye çalışılmaktadır. Web sitesi içerisinde bulunan ilgili materyaller, ilgili yasal kurumlar tarafından uygun görülmemesi durumda kaldırılacaktır."
General