Diğer programlama dillerinde olan çoklu kalıtım (multiple inheritance) özelliği Java programlama dilinde yoktur. Java programlama dilinde çoklu kalıtım desteğinden faydalanmak için arayüz (interface) ve dahili sınıflar (inner classes) kullanılır.
Bu makalemizde ise Bu iki destekten interface arayüz kavramını incelemeye çalışacağız.
Arayüz (Interface)
Arayüzler, soyut (abstract) sınıfların bir üst modeli gibi düşünülebilir, soyut sınıfların içerisinde hem iş yapan hem de hiçbir iş yapmayan sadece birleştirici rol üstlenen gövdesiz yordamlar (soyut yordamlar-abstract methods) vardı. Bu birleştirici rol oynayan yordamlar, soyut sınıftan (abstract class) türetilmiş alt sınıfların içerisinde iptal edilmeleri (override) gerektiğini polimorfizim makalemizde incelenmişti. Arayüzlerin içerisinde ise iş yapan herhangi bir yordam (method) bulunamaz; arayüzün içerisinde tamamen gövdesiz yordamlar (soyut yordamlar) bulunur. Bu açıdan bakılacak olursak, arayüzler, birleştirici bir rol oynamaları için tasarlanmıştır. Önemli bir noktayı hemen belirtelim; ara yüzlere ait gövdesiz (soyut) yordamlar otomatik olarak public erişim belirleyicisine sahip olurlar ve sizin bunu değiştirme imkânınız yoktur. Aynı şekilde arayüzlere ait global alanlarda otomatik public erişim belirleyicisine sahip olurlar ek olarak, bu alanlar yine otomatik olarak final ve statik özelliği içerirler ve sizin bunlara yine müdahale etme imkanınız yoktur.
Birleştiricilik
Bir önceki Java makalemiz olan Polimorfizm yazımızda verilen BüyükIsYeri örneğini, arayüzleri kullanarak baştan yazmadan önce, yeni UML diyagramını inceleyelim;
UML diyagramında görüldüğü üzere, Calisan arayüzü (interface), birleştirici bir rol oynamaktadır. Calisan arayüzünde tanımlanan ve soyut (gövdesiz) calis() yordamı (method), bu arayüze erişen tüm sınıfların içerisinde iptal edilmek zorundadır (override). UML diyagramımızı Java uygulamasına dönüştürülürse;
BuyukIsYeri.java
interface Calisan { // arayuz
public void calis() ;
}
class mudur implements Calisan {
public void calis() { // iptal etti (override)
System.out.println("Mudur Calisiyor");
}
}
class genelMudur extends Mudur {
public void calis() { // iptal etti (override)
System.out.println("GenelMudur Calisiyor");
}
public void toplantiYonet() {
System.out.println("GenelMudur toplanti yonetiyor");
}
}
class programci implements Calisan {
public void calis() { // iptal etti (override)
System.out.println("Programci Calisiyor");
}
}
class AnalizProgramci extends Programci {
public void analizYap() {
System.out.println("Analiz Yapiliyor");
}
}
class SistemProgramci extends Programci {
public void sistemIncele() {
System.out.println("Sistem Inceleniyor");
}
}
class Pazarlamaci implements Calisan {
public void calis() { // iptal etti (override)
System.out.println("Pazarlamaci Calisiyor");
}
}
class Sekreter implements Calisan {
public void calis() { // iptal etti (override)
System.out.println("Sekreter Calisiyor");
}
}
public class BuyukIsYeri {
public static void mesaiBasla(Calisan[] c ) {
for (int i = 0 ; i < c.length ; i++) {
c[i].calis(); // ! Dikkat !
}
}
public static void main(String args[]) {
Calisan[] c = new Calisan[6];
// c[0]=new Calisan(); ! Hata ! arayüz olusturulamaz
c[0]=new Programci(); // yukari cevirim (upcasting)
c[1]=new Pazarlamaci(); //yukari cevirim (upcasting)
c[2]=new Mudur(); //yukari cevirim (upcasting)
c[3]=new GenelMudur(); //yukari cevirim (upcasting)
c[4]=new AnalizProgramci(); //yukari cevirim (upcasting)
c[5]=new SistemProgramci(); //yukari cevirim (upcasting)
mesaiBasla(c);
}
} |
Yukarıdaki örneğimiz ilk etapta çekici gelmeyebilir, “Bunun aynısı soyut sınıflarla (abstract class) zaten yapılabiliyordu. Arayüzleri neden kullanayım ki... “ diyebilirsiniz. Yukarıdaki örnekte arayüzlerin nasıl kullanıldığı incelenmiştir; arayüzlerin sağladığı tüm faydalar birazdan daha detaylı bir şekilde incelenecektir.
Arayüzlerin devreye sokulmasını biraz daha yakından bakılırsa.
class Mudur implements Calisan {
public void calis() { // iptal etti (override)
System.out.println("Mudur Calisiyor");
}
} |
Olaylara Mudur sınıfının bakış açısından bakılsın. Bu sınıf Calisan arayüzünün gövdesiz yordamlarını iptal etmek (override) istiyorsa, Calisan arayüzüne ulaşması gerekir. Bu ulaşım implements anahtar kelimesi ile gerçekleşir. Mudur sınıfı bir kere Calisan arayüzüne ulaştımı, buradaki gövdesiz yordamları (soyut yordamları) kesin olarak iptal etmek (override) zorundadır. Uygulamanın çıktısı aşağıdaki gibidir;
Programci Calisiyor
Pazarlamaci Calisiyor
Mudur Calisiyor
GenelMudur Calisiyor
Programci Calisiyor
Programci Calisiyor
Arayüz (Interface) ve Soyut Sınıflar (Abstract Classes)
Eğer bir sınıf (soyut sınıflar dâhil) bir arayüze (interface) ulaşmak istiyorsa, bunu implements anahtar kelimesi ile gerçekleştirebileceğini belirtmiştik. Ayrıca eğer bir sınıf bir kere arayüze ulaşımı artık onun tüm gövdesiz yordamlarını (soyut yordamlar) kesin olarak iptal etmesi (override) gerektiğini de belirttik. Peki, eğer soyut bir sınıf (abstract class) bir arayüze ulaşırsa, arayüze ait gövdesiz yordamları kesin olarak, kendi içerisinde iptal etmeli mi? Bir örnek üzerinde incelersek;
hayvanat.java
interface Hayvan {
public void avlan() ;
}
abstract class Kedi implements Hayvan {
} |
Yukarıdaki örneğimizi derleyebilir (compile) miyiz? Derlense bile çalışma anında (run-time) hata oluşturur mu? Aslında olaylara kavramsal olarak bakıldığında çözüm yakalanmış olur. Soyut sınıfların amaçlarından biri aynı arayüz özelliğinde olduğu gibi birleştirici bir rol oynamaktır. Daha açık bir ifade kullanırsak, hem arayüzler olsun hem de soyut sınıflar olsun, bunların amaçları kendilerine ulaşan normal sınıflara, kendilerine ait olan gövdesiz yordamları iptal ettirmektir (override). O zaman yukarıdaki örnekte soyut olan Kedi sınıfı, Hayvan arayüzüne (interface) ait gövdesiz (soyut) avlan() yordamını iptal etmek zorunda değildir. Daha iyi anlaşılması açısından yukarıdaki örneği biraz daha geliştirelim ama öncesinde UML diyagramını çıkartalım;
UML diyagramından görüleceği üzere, Kaplan sınıfı, avlan() ve takipEt() yordamlarını (gövdesiz-soyut yordamlarını) iptal etmek zorundadır. UML diyagramını Java uygulamasına dönüştürülürse;
hayvanat2.java
interface Hayvan {
public void avlan() ;
}
abstract class Kedi implements Hayvan {
public abstract void takipEt() ;
}
class Kaplan extends Kedi {
public void avlan() { // iptal etti (override)
System.out.println("Kaplan avlaniyor...");
}
public void takipEt() { // iptal etti (override)
System.out.println("Kaplan takip ediyor...");
}
} |
Soyut (abstract) olan Kedi sınıfının içerisinde, herhangi bir gövdesiz yordam (soyut yordam) iptal edilmemiştir (override). İptal edilme işlemlerinin gerçekleştiği tek yer Kaplan sınıfının içerisidir. Soru: Kaplan sınıfı Hayvan arayüzünde (interface) tanımlanmış soyut olan (gövdesiz) avlan() yordamını iptal etmek (override) zorunda mı? Cevap: Evet, çünkü Kaplan sınıfı Kedi sınıfından türetilmiştir. Kedi sınıfı ise Hayvan arayüzüne ulaşmaktadır. Bu sebepten dolayı Kaplan sınıfının içerisinde avlan() yordamı iptal edilmelidir.
En baştaki sorumuzun cevabı olarak, hayvanat.java örneğimiz rahatlıkla derlenebilir (compile) ve çalışma anında (run-time) herhangi bir çalışma-anı istisnasına (runtime-exception) sebebiyet vermez. (Not: İstisnaları (Exception) bir sonraki makalemizde detaylı bir biçimde incelemeye çalışacağız.)
Arayüz (Interface) İle Çoklu Kalıtım (Multiple Inheritance)
İlk önce çoklu kalıtımın (multiple inheritance) niye tehlikeli ve Java programlama dili tarafından kabul görmediğini inceleyelim. Örneğin Programci soyut sınıfından türetilmiş iki adet sınıfımız bulunsun, Yazilimci ve Analist sınıfları. Bu iki sınıftan türetilen yeni bir sınıf olamaz mı? Örneğin CoderTurhal sınıfı; yani, CoderTurhal sınıfı aynı anda hem Yazilimci, hem de Analist sınıfından türetilebilir mi? Java programlama dilinde türetilemez. Bunun sebeplerini incelemeden evvel, hatalı yaklaşımı UML diyagramında ifade edilirse;
Java programlama dili niye çoklu kalıtımı bu şekilde desteklemez? UML diyagramını, hatalı bir Java uygulamasına dönüştürülürse;
Program.java
abstract class Programci {
public abstract void calis();
}
class Yazilimci extends Programci {
public void calis() {
System.out.println("Yazilimci calisiyor....") ;
}
}
class Analist extends Programci {
public void calis() {
System.out.println("Analist calisiyor....") ;
}
}
/*
Bu ornegimiz derleme aninda hata alicaktir.
Java, coklu kalitimi desteklemez
*/
class CoderTurhal extends Yazilimci, Analist {
} |
Program.java derleme anında hata alacaktır. Bu ufak ayrıntıyı belirttikten sonra, kaldığımız yerden devam edelim. Java’nın niye çoklu kalıtımı (multiple inheritance) desteklemediğini anlamak için aşağıdaki gösterim incelenmelidir.
Programci s = new CoderTurhal(); // yukari dogru cevirim
s.calis(); // ?? |
Herhangi bir yerden, yukarıdaki gösterimde belirtildiği gibi bir ifade yazılmış olsa, sonuç nasıl olurdu? Programci tipinde olan referans, CoderTurhal nesnesine bağlanmıştır (bağlanabilir çünkü arada kalıtım ilişkisi vardır). Fakat burada s.calis() ifadesi yazılırsa, hangi nesnenin calis() yordamı çağrılacaktır? Yazilimci nesnesinin mi? Yoksa Analist nesnesinin mi? Sonuçta, calıs() yordamı, Yazilimci ve Analist sınıflarının içerisinde iptal edilmiştir. Bu sorunun cevabı yoktur. Fakat çoklu kalıtımın bu zararlarından arıtılmış versiyonunu yani arayüzleri (interface) ve dâhili sınıflar (inner classes) kullanarak, diğer dillerinde bulunan çoklu kalıtım desteğini, Java programlama dilinde de bulmak mümkündür. ‘Peki ama nasıl?’ diyenler için hemen örneğimizi verelim. Yukarıdaki örneğimizi Java programlama diline uygun bir şekilde baştan yazalım ama öncesinde her zaman ki gibi işe UML diyagramını çizmekle başlayalım;
CoderTurhal, belki aynı anda hem Yazilimci hemde Analist olamaz ama onlara ait özelliklere sahip olabilir. Örneğin Yazilimci gibi buz üzerinde kayabilir ve Analist gibi şut atabilir. Yukarıdaki UML diyagramı Java uygulamasına dönüştürülürse.
Program2.java
interface ProgramYazabilme {
public void programYaz();
}
interface AnalizYapabilme {
public void analizYap();
}
class CoderTurhal implements ProgramYazabilme, AnalizYapabilme {
public void programYaz() {
System.out.println("CoderTurhal program yaziyor");
}
public void analizYap() {
System.out.println("CoderTurhal analiz yapıyor");
}
} |
Bu örneğimizde CoderTurhal, ProgramYazabilme ve AnalizYapabilme özelliklerine sahip olmuştur. Arayüzler içerisindeki (ProgramYazabilme,AnalizYapabilme) gövdesiz (soyut) yordamları (programYaz(), analizYap()), bu arayüzlere erişen sınıf tarafından kesinlikle iptal edilmelidir (overrride). Eğer iptal edilmez ise, derleme anında (compile-time) Java tarafından gerekli hata mesajı verilir.
Örneğimizden anlaşılabileceği üzere arayüz (interface) ile soyut (abstract) sınıf arasında büyük fark vardır. En başta kavramsal olarak bir fark vardır. Bu kavramsal fark nedir derseniz hemen açıklayalım; Soyut bir sınıftan türetilme yapıldığı zaman, türetilen sınıf ile soyut sınıf arasında mantıksal bir ilişki olması gerekirdi, örnek vermek gerekirse "Yarasa bir Hayvandır" gibi veya "Müdür bir Çalışandır" gibi… Geçen bölümlerde incelediğimiz bir ilişkisi. Fakat arayüzler ile bunlara erişen sınıflar arasında kalıtımsal bir ilişki bulunmayabilir.
Arayüzlerin Kalıtım (İnheritance) Yoluyla Genişletilmesi
Bir arayüz başka bir arayüzden türetilerek yeni özelliklere sahip olabilir; böylece arayüzler kalıtım yoluyla genişletilmiş olur. Olayları daha iyi anlayabilmek için önce UML diyagramını çizip sonrada Java uygulamasını yazalım.
Avlanabilme arayüzü, DahaHizliKosabilme arayüzünden türemiştir. DahaHizliKosabilme arayüzüde Kosabilme arayüzünde türemiştir. Yukarıdaki UML diyagramımızı Java uygulamasına dönüştürelim;
Jaguar.java
interface Kosabilme {
public void kos();
}
interface DahaHizliKosabilme extends Kosabilme {
public void dahaHizliKos();
}
interface Avlanabilme extends DahaHizliKosabilme {
public void avlan();
}
class RoadRunner implements DahaHizliKosabilme {
public void kos() {
System.out.println("RoadRunner kosuyor, bip ");
}
public void dahaHizliKos() {
System.out.println("RoadRunner kosuyor, bip bippp");
}
}
public class Jaguar implements Avlanabilme {
public void avlan() {
System.out.println("Juguar avlaniyor");
}
public void dahaHizliKos() {
System.out.println("Juguar daha hizli kos");
}
public void kos() {
System.out.println("Juguar Kosuyor");
}
} |
Jaguar sınıfı Avlanabilme arayüzüne ulaşarak, avlan(), dahaHizliKos(), kos() yordamlarını iptal etmek (override) zorunda bırakılmıştır. Bunun sebebi Avlanabilme arayüzünün DahaHizliKosabilme arayüzünden, DahaHizliKosabilme arayüzününde Kosabilme arayüzünden türemiş olmasıdır.
RoadRunner sınıfı ise sadece DahaHizliKosabilme arayüzüne erişerek, kos() ve dahaHizliKos() gövdesiz (soyut) yordamlarını iptal etmek (override) zorunda bırakılmıştır. Yine bunun sebebi DahaHizliKosabilme arayüzünün Kosabilme arayüzünden türemiş olmasıdır.
Arayüzlere Özel
Şimdi birazdan inceleyeceğiz olay sadece arayüzler söz konusu olduğunda yapılabilir. İlk olayımız açıklayalım; Bir arayüz (interface) birden çok arayüzden türetilebilir.
interface C {
//..
}
interface B {
//..
}
interface A extends B, C {
//..
} |
Yukarıdaki gösterimde, A arayüzü, birbirlerinden bağımsız iki arayüzden türetilmiş oldu. İkinci olay ise daha evvelde incelenmişti (bkz:Program2.java) ama bir kere daha üzerine basa basa durmakta fayda var; bir sınıf birden fazla arayüze rahatlıkla erişebilir.
Çakışmalar
Arayüzlerin içerisinde dönüş tipleri haricinde her şeyleri aynı olan gövdesiz (soyut) yordamlar varsa, bu durum beklenmedik sorunlara yol açabilir. Yazılanları Java uygulaması üzerinde gösterilirse;
Cakisma.java
interface A1 {
public void hesapla();
}
interface A2 {
public void hesapla(int d);
}
interface A3 {
public int hesapla();
}
class S1 implements A1,A2 { // sorunsuz
public void hesapla() { //adas yordamlar(overloaded)
System.out.println("S1.hesapla");
}
public void hesapla(int d) { //adas yordamlar(overloaded)
System.out.println("S1.hesapla " + d );
}
}
/*
! Hatali !, adas yordamlar (overloaded)
donus tiplerine gore ayirt edilemez
*/
class S2 implements A1,A3 {
public void hesapla() {
System.out.println("S2.hesapla");
}
/* ! Hata !
public int hesapla() {
System.out.println("S2.hesapla");
return 123;
}
*/
} |
Cakisma.java örneğini derlenirse (compile), aşağıdaki hata mesajı ile karşılaşılır:
Cakisma.java:27: hesapla() in S2 cannot implement hesapla() in A3; attempting to
use incompatible return type
found : void
required: int
class S2 implements A1,A3 {
^
1 error |
Bu hatanın oluşma sebebi, A1 ve A3 arayüzlerinin içerisindeki gövdesiz (soyut) yordamlarından kaynaklanır. Bu yordamların isimleri ve parametreleri aynıdır ama dönüş tipleri farklıdır.
public void hesapla(); // A1 arayüzüne ait
public int hesapla(); // A3 arayüzüne ait |
İki yordamın adaş yordam (overloaded method) olabilmesi için bu yordamların parametrelerinde kesin bir farklılık olması gerekirdi. İki yordamın dönüş tipleri dışında her şeyleri aynıysa bunlar adaş yordam olamazlar. Olamamalarının sebebi, Java’nın bu yordamları dönüş tiplerine göre ayırt edememesinden kaynaklanır.
Arayüzün (Interface) İçerisinde Alan Tanımlama
Arayüzlerin içerisinde gövdesiz (soyut) yordamların dışında alanlarda bulunabilir. Bu alanlar uygulamalarda sabit olarak kullanabilir. Çünkü arayüzün içerisinde tanımlanan bir alan (ilkel tipte veya sınıf tipinde olsun) otomatik olarak hem public erişim belirleyicisine hemde final ve static özelliğine sahip olur.
AyBul.java
interface Aylar {
int
OCAK = 1, SUBAT = 2, MART = 3,
NISAN = 4, MAYIS = 5, HAZIRAN = 6, TEMMUZ = 7,
AGUSTOS = 8, EYLUL = 9, EKIM = 10,
KASIM = 11, ARALIK = 12;
}
public class AyBul {
public static void main(String args[]) {
int ay = (int)(Math.random()*13) ;
System.out.println("Gelen ay = " + ay);
switch ( ay ) {
case Aylar.OCAK : System.out.println("Ocak");break;
case Aylar.SUBAT : System.out.println("Subat");break;
case Aylar.MART : System.out.println("Mart");break;
case Aylar.NISAN : System.out.println("Nisan");break;
case Aylar.MAYIS : System.out.println("Mayis");break;
case Aylar.HAZIRAN : System.out.println("Haziran");break;
case Aylar.TEMMUZ : System.out.println("Temmuz");break;
case Aylar.AGUSTOS : System.out.println("Agustos");break;
case Aylar.EYLUL : System.out.println("Eylul");break;
case Aylar.EKIM : System.out.println("Ekim");break;
case Aylar.KASIM : System.out.println("Kasim");break;
case Aylar.ARALIK : System.out.println("Aralik");break;
default:System.out.println("Tanimsiz Ay");
}
}
} |
Arayüzün İçerisinde Tanımlanmış Alanlara İlk Değerlerinin Verilmesi
Arayüzlerin içerisinde tanımlanmış alanların ilk değerleri, çalışma anında da (run-time) verilebilir. Aşağıdaki örneği inceleyelim.
Test.java
interface A7 {
int devir_sayisi = (int) ( Math.random() *6 ) ;
String isim = "A7" ;
double t1 = ( Math.random() * 8 ) ;
}
public class Test {
public static void main(String args[] ) {
System.out.println("devir_sayisi = " + A7.devir_sayisi );
System.out.println("isim = " + A7.isim );
System.out.println("t1 = " + A7.t1 );
}
} |
A7 arayüzünün içerisindeki ilkel (primitive) int tipindeki devir_sayisi ve t1 alanlarının değerlerini derleme anında bilebilmek imkânsızdır. Bu değerler ancak çalışma anında bilenebilir.
Dikkat edilmesi gereken bir başka husus ise A7 arayüzünün içerisindeki alanların ne zaman ilk değerlerini aldıklarıdır. Bir arayüzün içerisindeki alanlar final ve statik oldukları için, A7 arayüzüne ilk kez erişildiği zaman, A7 arayüzünün içerisindeki tüm alanlar ilk değerlerini alırlar.
Genel Bakış
Arayüzler ve soyut sınıfların bizlere sağlamak istediği fayda nedir? Aslında ulaşılmak istenen amaç çoklu yukarı çevirimdir (upcasting). Bir sınıfa ait nesnenin birçok tipteki sınıf referansına bağlanabilmesi, uygulama içerisinde büyük esneklik sağlar. Bir örnek üzerinde açıklayalım…
GenelBakis.java
interface Arayuz1 {
public void a1() ;
}
interface Arayuz2 {
public void a2() ;
}
abstract class Soyut1 {
public abstract void s1();
}
class A extends Soyut1 implements Arayuz1, Arayuz2 {
public void s1() { // iptal etti (override)
System.out.println("A.s1()");
}
public void a1() { // iptal etti (override)
System.out.println("A.a1()");
}
public void a2() { // iptal etti (override)
System.out.println("A.a2()");
}
}
public class GenelBakis {
public static void main(String args[]) {
Soyut1 soyut_1 = new A();
Arayuz1 arayuz_1 = (Arayuz1) soyut_1 ;
Arayuz2 arayuz_2 = (Arayuz2) soyut_1 ;
// Arayuz2 arayuz_2 = (Arayuz2) arayuz_1 ; // dogru
soyut_1.s1();
// soyut_1.a1(); // ! Hata !
// soyut_1.a2(); // ! Hata !
arayuz_1.a1();
// arayuz_1.a2(); // ! Hata !
// arayuz_1.s1(); // ! Hata !
arayuz_2.a2();
// arayuz_2.a1(); // ! Hata !
// arayuz_2.s1(); // ! Hata !
}
} |
A sınıfı Soyut1 soyut sınıfından türetilmiştir, ayrıca A sınıfı Arayuz1 ve Arayuz2 arayüzlerine (interface) erişmektedir. Daha yakından incelenirse,
class A extends Soyut1 implements Arayuz1, Arayuz2 { |
Yukarıdaki gösterim şunu der: A sınıfına ait bir nesne, Soyut1 sınıfı, Arayuz1 veya Arayuz2 arayüzü tipindeki referanslara bağlanabilir. Anlatılanlar UML diyagramına dönüştürülürse;
Bu örneğimizde görüldüğü üzere, A sınıfı ait tek bir nesne oluşturulmuştur. Oluşturulan nesne farklı referanslara bağlanabilmektir.
Soyut1 soyut_1 = new A();
Arayuz1 arayuz_1 = (Arayuz1) soyut_1 ; // tip degisimi
Arayuz2 arayuz_2 = (Arayuz2) soyut_1 ; // tip degisimi |
Oluşturulan tek bir A sınıfına ait nesne, birçok farklı sınıf ve arayüz tipindeki referansa bağlanabilmektedir. Örneğin yukarıdaki gösterimde, A sınıfına ait nesnemiz ilk olarak Soyut1 sınıfı tipindeki referansa bağlanmıştır. Bağlanabilir çünkü A sınıfı Soyut1 sınıfından türemiştir. Soyut1 sınıfı tipindeki referansa bağlı olan A sınıfına ait nesnenin sadece s1() yordamına ulaşılabilir.
Daha sonra Soyut1 referansının bağlı olduğu A sınıfına ait nesneyi, Arayuz1 tipindeki referansa bağlanıyor ama bu bağlama sırasında A sınıfına ait nesneyi Arayuz1 tipindeki referansa bağlanacağını açık açık söylemek gerekir. A sınıfına ait nesnemiz Arayuz1 tipindeki referansa bağlanırsa bu nesnenin sadece a1() yordamına erişilebilir.
Aynı şekilde Soyut1 sınıfı tipindeki referansa bağlı olan A sınıfına ait nesne, Arayuz2 tipindeki referansa bağlınabilir. A sınıfına ait nesne, Arayuz2 tipindeki referansa bağlanırsa, bu nesnenin sadece a2() yordamına erişebilir.
Soyut1 soyut_1 = new A();
Arayuz1 arayuz_1 = new A();
Arayuz2 arayuz_2 = new A(); |
Görüldüğü üzere, A sınıfına ait üç adet nesne oluşturduk ve bu nesnelerin her birini farklı tipteki referanslara bağlayabildik. Bu olay nesneye yönelik tasarımlar yaparken işimize çokça yarayabilecek bir yaklaşımdır.
Dâhili Arayüzler (Nested Interface)
Bir arayüz, başka bir arayüzün veya sınıfın içerisinde tanımlanabilir. Bir arayüzün içerisinde tanımlanan dâhili arayüzler, protected, friendly veya private erişim belirleyicisine sahip olamaz. Örneğimize geçmeden evvel UML diyagramını inceleyelim.
UML diyagramımızdan anlaşılacağı üzere, ArayuzA arayüzünün içerisinde iki adet dâhili arayüz (nested interface) tanımlanmıştır. Dışarıdaki iki sınıfımız, dâhili olarak tanımlanmış bu iki arayüze erişebilmektedir.
DahiliArayuzTest.java
interface ArayuzA { //aslinda public erisim belirleyicisine sahip
public interface DahiliArayuz1 {
public void isYap1() ;
}
/* // ! Hata !
protected interface DahiliArayuz2 {
public void isYap2() ;
}
*/
interface DahiliArayuz3 { // aslinda public erisim belirleyicisine sahip
public void isYap3() ;
}
/* // ! Hata !
private interface DahiliArayuz4 {
public void isYap4() ;
}
*/
}
class Erisim1 implements ArayuzA.DahiliArayuz1 {
public void isYap1() {
System.out.println("Erisim1.isYap1()");
}
}
class Erisim2 implements ArayuzA.DahiliArayuz3 {
public void isYap3() {
System.out.println("Erisim1.isYap3()");
}
}
public class DahiliArayuzTest {
public static void main(String args[]) {
Erisim1 e1 = new Erisim1();
Erisim2 e2 = new Erisim2();
e1.isYap1();
e2.isYap3();
}
} |
Dâhili arayüzlere erişen sınıflar açısından olaylar aynıdır. Yine bu dahili arayüzlerin içerisindeki gövdesiz yordamları iptal etmeleri gerekmektedir. Uygulamanın çıktısı aşağıdaki gibidir;
Erisim1.isYap1()
Erisim1.isYap3()
Sınıfların İçerisinde Tanımlanan Dahili Arayüzler (Nested Interface)
Bir arayüz diğer bir arayüzün içerisinde tanımlandığı gibi, bir sınıfın içerisinde de tanımlanabilir.
SinifA.java
public class SinifA {
public interface A1 {
public void ekranaBas();
} //arayüz
public class DahiliSinif1 implements A1 {
public void ekranaBas() {
System.out.println("DahiliSinif1.ekranaBas()");
}
} // class DahiliSinif1
protected class DahiliSinif2 implements A1 {
public void ekranaBas() {
System.out.println("DahiliSinif2.ekranaBas()");
}
} // class DahiliSinif2
class DahiliSinif3 implements A1 {
public void ekranaBas() {
System.out.println("DahiliSinif3.ekranaBas()");
}
} // class DahiliSinif3
private class DahiliSinif4 implements A1 {
public void ekranaBas() {
System.out.println("DahiliSinif4.ekranaBas()");
}
} // class DahiliSinif4
public static void main(String args[]) {
SinifA.DahiliSinif1 sad1 = new SinifA().new DahiliSinif1();
SinifA.DahiliSinif2 sad2 = new SinifA().new DahiliSinif2();
SinifA.DahiliSinif3 sad3 = new SinifA().new DahiliSinif3();
SinifA.DahiliSinif4 sad4 = new SinifA().new DahiliSinif4();
sad1.ekranaBas();
sad2.ekranaBas();
sad3.ekranaBas();
sad4.ekranaBas();
SinifB sb = new SinifB();
sb.ekranaBas();
}
}
class SinifB implements SinifA.A1{
public void ekranaBas() {
System.out.println("SinifB.ekranaBas()");
}
} |
SinifA sınıfının içerisinde tanımlanan A1 arayüzüne, SinifB sınıfından ulaşılabilir. Bir sınıfın içerisinde dahili arayüz tanımlanabildiği gibi dahili sınıfta tanımlanabilir. Bu konu az sonra incelenecektir. Bu örneğimizdeki ana fikir, bir sınıfın içerisinde nasıl dahili arayüzün oluşturulduğu ve bu dahili arayüzün, dahili sınıf olsun veya dışarıdan başka bir sınıf tarafından olsun, nasıl erişilebildiğini göstermektir.
Toparlamak gerekirse bu makalemizde Java ise arayüz kavramını ayrıntılı bir biçimde incelemeye çalıştık. Bir sonraki Java makalemizde ise Java ile dahili sınıflar kavramına ayrıntılı bir biçimde değinmeye çalışacağız.
Umarım Yararlı olmuştur.
Alıntıdır: Altuğ Bilgin Altıntaş
Turhal Temizer
http://turhal.blogspot.com