Bölüm 4: Soyut Sınıflar ve Arayüzler

Java ile Nesne Yönelimli Programlama
SoyutlamaSoyut sınıflarSoyut metotlarArayüzler implements ve çoklu arayüzdefault, static, private metotlarsealed, non-sealed, permitsArayüz mü, soyut sınıf mı?
Sercan KÜLCÜ · Giresun Üniversitesi · Elektrik-Elektronik Mühendisliği

Giriş

Öğrenme Çıktıları

Soyut sınıf

abstract sınıf ve metot tanımlamak; soyut sınıftan neden nesne oluşturulamadığını ve alt sınıfın neyi gerçeklemek zorunda olduğunu açıklamak.

Dinamik bağlama

Soyut türden bir referansla çağrılan metodun, çalışma anında nesnenin gerçek sınıfından seçildiğini kodu izleyerek göstermek.

Arayüz

interface tanımlamak, implements ile bir ya da birden çok arayüzü gerçeklemek; default, static ve private metotları kullanmak.

Tasarım kararı

Mühürlü (sealed) türleri tanımak; bir problem için arayüz mü soyut sınıf mı (ya da ikisi birden) gerektiğine karar vermek.

Soyutlama

Soyutlama (Abstraction)

  • Soyutlama: karmaşık ayrıntıları gizleyip yalnızca temel özellikleri göstermek.
  • Araba kullanan kişi direksiyonu ve pedalları bilir; motorun içini bilmek zorunda değildir.
  • “Şekil” bir kavramdır: her şeklin alanı vardır, ama genel bir şeklin alanı hesaplanamaz. Formülü yalnızca Kare, Çember, Üçgen bilir.
  • Java'da soyutlamanın iki aracı vardır: soyut sınıflar (abstract class) ve arayüzler (interface).
  • İkisi de ne yapılacağını söyler; nasıl yapılacağını alt sınıflara bırakır.
Şekilalanı var, çizilebilirKarea · aÇemberπ · r²Üçgent · h / 2

Ortak kavram yukarıda, ayrıntı aşağıda.

Soyut Sınıflar

Soyut Sınıf (Abstract Class)

  • abstract anahtar kelimesiyle tanımlanır: public abstract class Sekil.
  • Hem soyut (gövdesiz) hem de somut (concrete, gövdeli) metotlar içerebilir.
public abstract class Sekil {
    // Soyut olmayan (somut) metot
    void tasi(int x, int y) {
        System.out.println("(" + x + ", " + y + ") noktasına taşındı");
    }
    // Soyut metot (alt sınıflarca gerçeklenmeli)
    abstract void ciz();
}
Somut metot: tasi

Gövdesi hazırdır. Alt sınıflar onu olduğu gibi miras alır; isterse geçersiz kılar (override).

Soyut metot: ciz

Yalnızca imzası vardır. Her somut alt sınıf kendi ciz() gövdesini yazmak zorundadır.

Soyut sınıf yarım kalmış bir plan gibidir: bazı odalar bitmiş, bazılarının yalnızca yeri belli.

Soyut Sınıflar

Soyut Sınıfın Özellikleri

  • Soyut sınıftan nesne oluşturulamaz (örneği, instance'ı olamaz).
  • Başka sınıflar onu extends ile genişletir (kalıtım).
  • Somut bir alt sınıf, tüm soyut metotları gerçeklemek (implement) zorundadır.
  • Ortak durumu (alanlar) ve davranışları tek yerde toplar → kod yeniden kullanılır.
  • Alt sınıflar soyut metotları kendi ihtiyacına göre doldurur.
  • Yapıcı metotları (constructor) olabilir; alt sınıf onları super(...) ile çağırır.
«abstract»Sekil- isim : String+ Sekil(isim)+ getIsim() : String+ alan() : double+ ciz()Kare- kenar+ alan(): doubleCember- r+ alan(): doubleDikdortgen- genislik- yukseklik+ alan(): double

UML'de soyut sınıf ve soyut metot eğik yazılır.

Soyut Sınıflar

Soyut Sınıftan Nesne Oluşturulamaz

Sekil s1 = new Sekil("?");     // HATA
Sekil s2 = new Kare(3);        // Doğru: değişkenin türü soyut olabilir
Sekil[] dizi = new Sekil[3];   // Doğru: dizi yalnızca referans tutar

Derleyici mesajı (1. satır)

error: Sekil is abstract; cannot be instantiated
Neden yasak?

Sekil'in alan() metodunun gövdesi yok. new Sekil() olsaydı s1.alan() çağrısında çalıştırılacak kod bulunmazdı.

Referans türü olarak serbest

Değişken, parametre, dizi elemanı ve dönüş türü Sekil olabilir. new Sekil[3] nesne değil, 3 boş (null) referans oluşturur.

abstract final class yazılamaz: final genişletmeyi yasaklar, abstract genişletmeyi gerektirir → illegal combination of modifiers: abstract and final.

Soyut Sınıflar

Soyut Metotlar (Abstract Methods)

  • Gerçekleştirim (implementasyon) olmadan tanımlanır.
  • Süslü parantez içermez, noktalı virgülle biter:
public abstract void ciz();
  • Soyut metodu olan sınıf soyut olmak zorundadır.
  • Soyut sınıflar genellikle kalıtım hiyerarşisinin tepesinde yer alır.
public abstract class Sekil {
    // Soyut metot
    public abstract void ciz();
}

// Ayrı bir dosyada:
public class Cember extends Sekil {
    // ciz metodunu gerçeklemek zorunlu
    public void ciz() {
        System.out.println("Çember çiziliyor");
    }
}

new Cember().ciz(); çıktısı

Çember çiziliyor

Soyut Sınıflar

Soyut Metot Kuralları

YazılanSonuç
abstract void ciz(); (soyut sınıfta)✓ Doğru
abstract void ciz() { }abstract methods cannot have a body
class Sekil { abstract void ciz(); }Sekil is not abstract and does not override abstract method ciz() in Sekil
class Cember extends Sekil { } (ciz yok)Cember is not abstract and does not override abstract method ciz() in Sekil
private abstract, static abstract, final abstractillegal combination of modifiers: abstract and private (static, final için de aynı)
abstract class Cember extends Sekil { }✓ Doğru: soyut alt sınıf, gerçeklemeyi kendi alt sınıflarına bırakabilir
abstract class A { void f() { } }✓ Doğru: hiç soyut metodu olmayan soyut sınıf da yazılabilir (yine de new A() yapılamaz)

Neden private/static/final olmaz? Üçü de metodun alt sınıfta geçersiz kılınmasını engeller; soyut metot ise geçersiz kılınmak için vardır.

Soyut Sınıflar · Kodu adım adım çalıştır

new Kare(3): Yapıcı Zinciri

YığınÖbek (heap)mains ?Kare nesnesiSekil'den gelenisim = nullKare'ye aitkenar = 0.0

new Kare(3): önce öbekte (heap) bir Kare nesnesi ayrılır. Alanlar varsayılan değer alır: null ve 0.0.

YığınÖbek (heap)mains ?Kare nesnesiSekil'den gelenisim = nullKare'ye aitkenar = 0.0

Kare'nin yapıcısı çalışır. Parametre kenar = 3.0 (int 3, double'a genişletildi).

YığınÖbek (heap)mains ?Kare nesnesiSekil'den gelenisim = nullKare'ye aitkenar = 0.0

İlk iş super("Kare"): üst sınıfın yapıcısı çağrılır. Soyut sınıfın da yapıcısı olabilir.

YığınÖbek (heap)mains ?Kare nesnesiSekil'den gelenisim = nullKare'ye aitkenar = 0.0

Sekil yapıcısı çalışıyor. Ayrı bir Sekil nesnesi oluşmaz: aynı nesnenin Sekil'den gelen kısmı kuruluyor.

YığınÖbek (heap)mains ?Kare nesnesiSekil'den gelenisim = "Kare"Kare'ye aitkenar = 0.0

this.isim = isim: nesnenin isim alanı "Kare" olur.

YığınÖbek (heap)mains ?Kare nesnesiSekil'den gelenisim = "Kare"Kare'ye aitkenar = 0.0

Mesaj yazdırılır. Sekil yapıcısı biter, çerçevesi yığından çıkar.

YığınÖbek (heap)mains ?Kare nesnesiSekil'den gelenisim = "Kare"Kare'ye aitkenar = 3.0

Kare yapıcısına dönülür: this.kenar = 3.0. Nesne artık tamamen kurulmuştur.

YığınÖbek (heap)mains •Kare nesnesiSekil'den gelenisim = "Kare"Kare'ye aitkenar = 3.0

Nesnenin referansı s'ye atanır. Değişkenin türü Sekil (soyut), gösterdiği nesne Kare.

YığınÖbek (heap)mains •Kare nesnesiSekil'den gelenisim = "Kare"Kare'ye aitkenar = 3.0

s.alan(): Sekil'de alan() soyut. Çalışma anında nesnenin türüne bakılır → Kare.alan(): 3.0 · 3.0.

YığınÖbek (heap)mains •Kare nesnesiSekil'den gelenisim = "Kare"Kare'ye aitkenar = 3.0

Dönen 9.0 yazdırılır.

abstract class Sekil {
    private String isim;
    Sekil(String isim) {
        this.isim = isim;
        System.out.println(isim + " kuruldu");
    }
    abstract double alan();
}
class Kare extends Sekil {
    private double kenar;
    Kare(double kenar) {
        super("Kare");
        this.kenar = kenar;
    }
    double alan() { return kenar * kenar; }
}
// main içinde:
Sekil s = new Kare(3);
System.out.println(s.alan());

Soyut Sınıflar · Kodu adım adım çalıştır

Soyut Türde Dizi: Dinamik Bağlama

dizi•••[0][1]Sekil[]Kareisim = "Kare"kenar = 2.0Cemberisim = "Çember"r = 1.0

Dizi iki referans tutar. Elemanların türü Sekil, gösterdikleri nesneler Kare ve Cember.

dizi•••[0][1]Sekil[]Kareisim = "Kare"kenar = 2.0Cemberisim = "Çember"r = 1.0

1. tur: t, dizi[0]'daki Kare nesnesini gösterir.

dizi•••[0][1]Sekil[]Kareisim = "Kare"kenar = 2.0Cemberisim = "Çember"r = 1.0

t.alan(): derleyici yalnızca Sekil'de alan() olduğunu bilir; JVM nesneye bakar → Kare.alan(): 2.0 · 2.0.

dizi•••[0][1]Sekil[]Kareisim = "Kare"kenar = 2.0Cemberisim = "Çember"r = 1.0

Dönen 4.0 yazdırılır.

dizi•••[0][1]Sekil[]Kareisim = "Kare"kenar = 2.0Cemberisim = "Çember"r = 1.0

Kare ciz()'i geçersiz kılmadı (override etmedi) → Sekil'deki somut ciz() miras olarak çalışır.

dizi•••[0][1]Sekil[]Kareisim = "Kare"kenar = 2.0Cemberisim = "Çember"r = 1.0

2. tur: t artık Cember nesnesini gösterir. Aynı satır, farklı nesne.

dizi•••[0][1]Sekil[]Kareisim = "Kare"kenar = 2.0Cemberisim = "Çember"r = 1.0

Bu kez Cember.alan() seçilir: π · 1.0 · 1.0.

dizi•••[0][1]Sekil[]Kareisim = "Kare"kenar = 2.0Cemberisim = "Çember"r = 1.0

Dönen değer yazdırılır: Math.PI.

dizi•••[0][1]Sekil[]Kareisim = "Kare"kenar = 2.0Cemberisim = "Çember"r = 1.0

Cember ciz()'i geçersiz kılmış → kendi sürümü: dinamik bağlama (dynamic binding).

dizi•••[0][1]Sekil[]Kareisim = "Kare"kenar = 2.0Cemberisim = "Çember"r = 1.0

Eleman kalmadı, döngü biter. Yeni bir şekil (ör. Ucgen) eklemek için bu döngüyü değiştirmek gerekmez.

abstract class Sekil {
    // isim alanı ve yapıcısı
    abstract double alan();
    void ciz() {
        System.out.println(isim + " çiziliyor");
    }
}
class Kare extends Sekil {
    // kenar alanı ve yapıcısı
    double alan() { return kenar * kenar; }
}
class Cember extends Sekil {
    // r alanı ve yapıcısı
    double alan() { return Math.PI * r * r; }
    void ciz() {
        System.out.println("Çember, r = " + r);
    }
}
// main içinde:
Sekil[] dizi = { new Kare(2), new Cember(1) };
for (Sekil t : dizi) {
    System.out.println(t.alan());
    t.ciz();
}

Soyut Sınıflar · Kodu adım adım çalıştır

Somut Metot Soyut Metodu Çağırır

«abstract»Icecek+ hazirla() {final}+ suyuKaynat()+ demle()Cay+ demle()

Değişken türü Icecek (soyut), nesne Cay. Cay tek soyut metodu demle()'yi gerçeklediği için somuttur.

«abstract»Icecek+ hazirla() {final}+ suyuKaynat()+ demle()Cay+ demle()

Cay'da hazirla() yok → Icecek'ten miras alınan somut metot çalışır. final: alt sınıflar bu sırayı değiştiremez.

«abstract»Icecek+ hazirla() {final}+ suyuKaynat()+ demle()Cay+ demle()

1. adım: suyuKaynat(), yani this.suyuKaynat() çağrılır.

«abstract»Icecek+ hazirla() {final}+ suyuKaynat()+ demle()Cay+ demle()

Icecek'teki somut suyuKaynat() çalışır.

«abstract»Icecek+ hazirla() {final}+ suyuKaynat()+ demle()Cay+ demle()

2. adım: demle(). Icecek'te gövdesi yok, ama this bir Cay nesnesi…

«abstract»Icecek+ hazirla() {final}+ suyuKaynat()+ demle()Cay+ demle()

…bu yüzden Cay.demle() çalışır. Soyut sınıftaki kod, henüz yazılmamış alt sınıf kodunu çağırabilir.

«abstract»Icecek+ hazirla() {final}+ suyuKaynat()+ demle()Cay+ demle()

3. adım: servis mesajı yazdırılır.

«abstract»Icecek+ hazirla() {final}+ suyuKaynat()+ demle()Cay+ demle()

İskelet soyut sınıfta, değişen adım alt sınıfta: şablon metot (template method). Bir Kahve sınıfı yalnızca demle()'yi yazar.

abstract class Icecek {
    final void hazirla() {
        suyuKaynat();
        demle();
        System.out.println("Servis edildi");
    }
    void suyuKaynat() {
        System.out.println("Su kaynadı");
    }
    abstract void demle();
}
class Cay extends Icecek {
    void demle() {
        System.out.println("Çay demlendi");
    }
}
// main içinde:
Icecek i = new Cay();
i.hazirla();

Arayüzler

Arayüz (Interface) Nedir?

  • Bir sistemin belirli işlevlere sahip olacağını garanti eden sözleşmedir (contract).
  • İki sistem arasındaki entegrasyon noktasını temsil eder: kullanan taraf yalnızca arayüzü bilir.
  • interface anahtar kelimesiyle tanımlanır.
  • Arayüzdeki metotlar, public yazılsın ya da yazılmasın public'tir.
  • static, default ya da private değilse metot otomatik olarak abstract'tır.
  • Alan olarak yalnızca public static final sabitler içerebilir.
Benzetme: priz ve fiş

Priz bir sözleşmedir: hangi cihaz takılırsa takılsın aynı gerilimi verir. Ütü de bilgisayar da prizin standardına uyar; şebeke cihazın içini, cihaz şebekenin içini bilmez.

Soyut sınıf “bir türüdür” (is-a) ilişkisi kurar; arayüz “yapabilir” (can-do) yeteneği tanımlar.

Arayüzler

Arayüz Tanımlama

Yazdığımız

public interface GeometrikSekil {
    // Soyut metot (public abstract)
    void ciz();
    // Sabit (public static final)
    int KENAR_SAYISI = 4;
}

Derleyicinin gördüğü

public interface GeometrikSekil {
    public abstract void ciz();
    public static final int KENAR_SAYISI = 4;
}
  • Sabitlere arayüz adıyla erişilir: GeometrikSekil.KENAR_SAYISI → 4. Adları BÜYÜK_HARF ile yazılır.
  • Sabite değer vermek zorunludur: int deger; → error: = expected.
  • Sabit değiştirilemez: GeometrikSekil.KENAR_SAYISI = 5; → error: cannot assign a value to static final variable KENAR_SAYISI.
  • Arayüzden de nesne oluşturulamaz: new GeometrikSekil() → error: GeometrikSekil is abstract; cannot be instantiated.

Arayüzler

Arayüz Gerçekleme (implements)

  • Bir sınıf, bir arayüzü implements anahtar kelimesiyle gerçekler.
  • Arayüzdeki tüm soyut metotları gerçeklemeli ya da kendisi abstract olmalıdır.
  • Eksik metot: Kare is not abstract and does not override abstract method ciz() in GeometrikSekil.
  • Gerçekleyen metot public olmalıdır. void ciz() yazılırsa: attempting to assign weaker access privileges; was public.
  • Arayüz türünde değişken, onu gerçekleyen her sınıfın nesnesini gösterebilir.
public class Kare implements GeometrikSekil {
    // ciz metodunu gerçeklemek zorunlu
    public void ciz() {
        System.out.println("Kare çiziliyor");
    }
}
// main içinde:
GeometrikSekil g = new Kare();
g.ciz();
System.out.println(GeometrikSekil.KENAR_SAYISI);

Çıktı

Kare çiziliyor
4

Arayüzler

Bir Sınıf, Birden Fazla Arayüz

  • Bir sınıf en fazla bir üst sınıfı genişletebilir (extends)…
  • …ama sıfır ya da daha çok arayüzü gerçekleyebilir (implements A, B, …).
  • Önce extends, sonra implements yazılır.
  • class C extends A, B → error: '{' expected.
Hayvan~ isim : String~ Hayvan(isim)«interface»Ucabilir+ uc()«interface»Yuzebilir+ yuz()Ordek~ Ordek(isim)+ uc()+ yuz()
class Hayvan {
    String isim;
    Hayvan(String isim) { this.isim = isim; }
}
interface Ucabilir { void uc(); }
interface Yuzebilir { void yuz(); }
class Ordek extends Hayvan implements Ucabilir, Yuzebilir {
    Ordek(String isim) { super(isim); }
    public void uc() { System.out.println(isim + " uçuyor"); }
    public void yuz() { System.out.println(isim + " yüzüyor"); }
}

Arayüzler · Kodu adım adım çalıştır

Aynı Nesne, Farklı Arayüz Referansları

Yığın: mainÖbek (heap)Ordek o•Ucabilir u—Yuzebilir y—Ordek nesnesiisim = "Donald"uc()yuz()

Öbekte tek bir Ordek nesnesi oluşur; o onu gösterir.

Yığın: mainÖbek (heap)Ordek o•Ucabilir u•Yuzebilir y—Ordek nesnesiisim = "Donald"uc()yuz()

Atama nesneyi kopyalamaz: u aynı nesneyi gösterir. Ordek bir Ucabilir'dir → dönüşüm otomatik.

Yığın: mainÖbek (heap)Ordek o•Ucabilir u•Yuzebilir y•Ordek nesnesiisim = "Donald"uc()yuz()

y da aynı nesneyi gösterir. Bir nesne, üç farklı türden “bakış”.

Yığın: mainÖbek (heap)Ordek o•Ucabilir u•Yuzebilir y•Ordek nesnesiisim = "Donald"uc()yuz()

u.uc(): u'nun türü Ucabilir; ondan yalnızca uc() çağrılabilir. Çalışan, nesnenin kendi uc()'su.

Yığın: mainÖbek (heap)Ordek o•Ucabilir u•Yuzebilir y•Ordek nesnesiisim = "Donald"uc()yuz()

y.yuz(): Yuzebilir türü yuz()'ü bilir.

Yığın: mainÖbek (heap)Ordek o•Ucabilir u•Yuzebilir y•Ordek nesnesiisim = "Donald"uc()yuz()

u.yuz() yazılsaydı: error: cannot find symbol. Nesnede yuz() var, ama derleyici referansın türüne bakar.

Yığın: mainÖbek (heap)Ordek o•Ucabilir u•Yuzebilir y•Ordek nesnesiisim = "Donald"uc()yuz()

o'nun türü Ordek: alan isim dahil her şey erişilebilir.

Yığın: mainÖbek (heap)Ordek o•Ucabilir u•Yuzebilir y•Ordek nesnesiisim = "Donald"uc()yuz()

== referansları karşılaştırır: hepsi aynı nesneyi gösterir → true.

Ordek o = new Ordek("Donald");
Ucabilir u = o;
Yuzebilir y = o;
u.uc();
y.yuz();
// u.yuz();   derleme hatası!
System.out.println(o.isim);
System.out.println(u == o);

Arayüzler

Arayüzden Kalıtım

Bir arayüz, başka bir arayüzü extends ile genişletebilir; virgülle birden çok arayüzü de genişletebilir: interface C extends A, B.

public interface KareSekli extends GeometrikSekil {
    // Ek metotlar veya sabitler eklenebilir
    void ozelFonksiyon();
}
class OzelKare implements KareSekli {
    public void ciz() {
        System.out.println("Özel kare çiziliyor");
    }
    public void ozelFonksiyon() {
        System.out.println("Köşegen: " + Math.sqrt(2));
    }
}
// main içinde:
KareSekli k = new OzelKare();
k.ciz();
k.ozelFonksiyon();
GeometrikSekil g = k;   // üst arayüze atanabilir
«interface»GeometrikSekil+ KENAR_SAYISI : int = 4+ ciz()«interface»KareSekli+ ozelFonksiyon()OzelKare+ ciz()+ ozelFonksiyon()extendsimplements

Çıktı

Özel kare çiziliyor
Köşegen: 1.4142135623730951

Arayüz Metotları

Varsayılan Metotlar (default, Java 8+)

  • Arayüz, gövdesi olan varsayılan (default) metotlara sahip olabilir.
  • Gerçekleyen sınıf onu miras alır; isterse geçersiz kılar. Amaç: arayüze yeni metot eklerken onu gerçekleyen eski sınıfları bozmamak.
interface Collection {
    void add(Object o);
    void remove(Object o);
    int size();
    default boolean isEmpty() {
        return size() == 0;
    }
}

Çıktı

true
2 false
class Sepet implements Collection {
    private int adet = 0;
    public void add(Object o) { adet++; }
    public void remove(Object o) { adet--; }
    public int size() { return adet; }
    // isEmpty() yazılmadı: default sürüm kullanılır
}
// main içinde:
Sepet sepet = new Sepet();
System.out.println(sepet.isEmpty());
sepet.add("elma");
sepet.add("armut");
System.out.println(sepet.size() + " " + sepet.isEmpty());

isEmpty(), soyut size()'ı çağırır; hangi size()'ın çalışacağı nesneye göre belirlenir. (Bu Collection bizim arayüzümüzdür; java.util.Collection Bölüm 5'te.)

Arayüz Metotları

Statik Metotlar (static, Java 8+)

interface Geometry {
    static double circleArea(double r) { return Math.PI * r * r; }
    static double squareArea(double s) { return s * s; }
    static double perimeter(double s) { return s * 4.0; }
    static double boxVolume(double w, double h, double d) {
        return w * h * d;
    }
}
// main içinde:
System.out.println(Geometry.circleArea(2));
System.out.println(Geometry.squareArea(3));
System.out.println(Geometry.perimeter(2.5));
System.out.println(Geometry.boxVolume(2, 3, 4));

Çıktı

12.566370614359172
9.0
10.0
24.0
  • Nesneye bağlı olmayan yardımcı işlevler; yalnızca arayüz adıyla çağrılır: Geometry.circleArea(2).
  • Gerçekleyen sınıfa miras geçmez: Daire implements Geometry olsa da Daire.squareArea(2) → cannot find symbol.
  • Yalnız PI yazılırsa cannot find symbol; sabit Math sınıfındadır: Math.PI.

Arayüz Metotları

Default Metot Çakışması

  • İki arayüz aynı imzalı default metot getirirse sınıf hangisini alacağını bilemez; metodu kendisi yazmak (geçersiz kılmak) zorundadır.
  • İstenen arayüzün sürümü ArayuzAdi.super.metot() ile çağrılabilir.
interface Selamlayan {
    default void selam() { System.out.println("Merhaba"); }
}
interface Karsilayan {
    default void selam() { System.out.println("Hoş geldiniz"); }
}
class Gorevli implements Selamlayan, Karsilayan {
    public void selam() {       // geçersiz kılmak zorunlu
        Selamlayan.super.selam();
        Karsilayan.super.selam();
    }
}

new Gorevli().selam();

Merhaba
Hoş geldiniz

selam() yazılmasaydı

error: types Selamlayan and Karsilayan are incompatible;
  class Gorevli inherits unrelated defaults for selam()
  from types Selamlayan and Karsilayan

Arayüz Metotları

Özel (private) Arayüz Metotları (Java 9+)

interface Yazdirilabilir {
    String icerik();
    default void yazdir() {
        System.out.println(baslik() + ": " + icerik());
    }
    private String baslik() {   // Java 9+
        return "Belge";
    }
}
class Rapor implements Yazdirilabilir {
    public String icerik() { return "Vize sonuçları"; }
}

new Rapor().yazdir();

Belge: Vize sonuçları
  • private metotlar, default (ve static) metotların ortak yardımcı kodunu dışarı açmadan paylaşır.
  • Gövdeleri vardır ve yalnızca arayüzün içinden çağrılabilir.
  • Gerçekleyen sınıf onları göremez: Rapor içinde baslik() çağrılamaz.
  • Akış: yazdir() (default) → baslik() (private) + icerik() (Rapor'un gerçeklediği soyut metot).

Arayüz Metotları

Arayüz Üyeleri: Özet Tablosu

ÜyeÖrnekÖrtük belirteçlerGövdeJava
Soyut metotvoid ciz();public abstractyok1.0
Sabitint KENAR_SAYISI = 4;public static final—1.0
Varsayılan metotdefault boolean isEmpty() {…}publicvar8
Statik metotstatic double squareArea(…) {…}publicvar8
Özel metotprivate String baslik() {…}yalnızca arayüz içindevar9
Örnek alanı, yapıcı—İzin verilmez: arayüzün nesnesi yoktur, durum (state) tutamaz
Gerçekleyen sınıf için kural: arayüzdeki tüm soyut metotları public olarak yazmak. Default metotlar isteğe bağlı, static ve private metotlar zaten miras geçmez.

Mühürlü Türler

Mühürleme: sealed, non-sealed, permits

  • Sınıflar ve arayüzler mühürlenebilir (Java 17+).
  • sealed: türü yalnızca izin verilen alt türler genişletebilir ya da gerçekleyebilir.
  • permits: izin verilen alt türlerin listesi.
  • İzin verilen her alt tür birini seçmek zorundadır:
    final: hiyerarşi burada biter;
    sealed: yeniden kısıtlar;
    non-sealed: kısıtı kaldırır, herkes genişletebilir.
  • Yarar: derleyici tüm alt türleri bilir (kapalı hiyerarşi).
«sealed interface»Hayvan+ hareketEt()«final»Kedi+ hareketEt()«non-sealed»Kopek+ hareketEt()Kangal+ hareketEt()Kusizin yok ✗permits Kedi, Kopek

Mühürlü Türler

Mühürlü Arayüz Örneği

public sealed interface Hayvan permits Kedi, Kopek {
    void hareketEt();
}
final class Kedi implements Hayvan {
    public void hareketEt() {
        System.out.println("Kedi sessizce yürüyor");
    }
}
non-sealed class Kopek implements Hayvan {
    public void hareketEt() {
        System.out.println("Köpek koşuyor");
    }
}
class Kangal extends Kopek {   // serbest
    public void hareketEt() {
        System.out.println("Kangal sürüyü koruyor");
    }
}
// Java 21: switch tüm alt türleri kapsar,
// default dalı gerekmez
static String tur(Hayvan h) {
    return switch (h) {
        case Kedi k -> "kedi";
        case Kopek k -> "köpek";
    };
}
// main içinde:
Hayvan[] hayvanlar =
    { new Kedi(), new Kopek(), new Kangal() };
for (Hayvan h : hayvanlar) {
    h.hareketEt();
    System.out.println(tur(h));
}

Çıktı

Kedi sessizce yürüyor
kedi
Köpek koşuyor
köpek
Kangal sürüyü koruyor
köpek

Karşılaştırma

Arayüz ve Soyut Sınıf Karşılaştırması

ÖzellikArayüzSoyut sınıf
Tanım / kullanıminterface · implementsabstract class · extends
MetotlarSoyut; Java 8+ default ve static, Java 9+ privateHem soyut hem somut metotlar
AlanlarYalnızca public static final sabitler; örnek alanı (instance field) olamazHer türlü alan (örnek alanları dahil)
YapıcıYokOlabilir; alt sınıf super(…) ile çağırır
ErişimÜyeler örtük public (private yardımcılar hariç)public, protected, private, paket
KalıtımBir sınıf çok sayıda arayüz gerçekler; arayüz birden çok arayüzü genişletir → çoklu kalıtım özelliğiBir sınıf yalnızca tek bir sınıftan türetilir
Amaçİlgisiz sınıflara ortak yetenek (“yapabilir”)Ortak niteliklere sahip sınıfları gruplamak (“bir türüdür”)
Nesneİkisinden de new ile nesne oluşturulamaz; ikisi de referans türü olarak kullanılır

Karşılaştırma

Hangisini Seçmeli?

Soyut sınıf seçin

  • Alt sınıflar yakın akraba: Kare, Çember birer Sekil'dir.
  • Ortak alanlar ve yapıcı gerekiyor (isim).
  • Ortak kodun bir kısmı hazır (şablon metot).

Arayüz seçin

  • İlgisiz sınıflara aynı yetenek: Kus, Ucak, Drone hepsi Ucabilir.
  • Bir sınıfa birden çok yetenek kazandırmak.
  • Kullanan kodla gerçekleyen kod arasında sözleşme (API).

Çoğu zaman ikisi birlikte kullanılır:

abstract class Hayvan implements Sesli { … }
class Ordek extends Hayvan implements Ucabilir, Yuzebilir { … }
Java, sınıflarda çoklu kalıtıma izin vermez: iki üst sınıf aynı alanı ya da metodu farklı tanımlarsa hangisinin geçerli olduğu belirsizleşir (elmas problemi). Arayüzler durum taşımadığı için bu sorun büyük ölçüde ortadan kalkar; kalan default çakışmasını derleyici size çözdürür.

Özet

Özet

KavramÖzü
SoyutlamaAyrıntıyı gizleyip “ne yapılır”ı tanımlamak; “nasıl”ı alt sınıflara bırakmak
Soyut sınıfabstract class; nesnesi olmaz; alan, yapıcı, soyut ve somut metot içerebilir
Soyut metotGövdesiz, ; ile biter; somut alt sınıf gerçeklemek zorunda
Dinamik bağlamaDerleyici referans türüne, JVM nesnenin gerçek türüne bakar
ArayüzSözleşme; metotlar örtük public abstract, alanlar public static final
implementsTek extends, çok sayıda implements; gerçekleyen metot public
default / static / privateGövdeli arayüz metotları (Java 8 / 8 / 9)
sealedpermits ile izinli alt türler; her biri final, sealed ya da non-sealed

Özet

İlgili Sayfalar

Java Interfaces

Arayüzler, çoklu gerçekleme, default, static ve private metotlar; ödeme sistemi örneği (İngilizce).

Generic Programming

Genel (generic) sınıflar ve metotlar, sınırlı tür parametreleri, joker türler (İngilizce).

Java Annotations

@Override gibi açıklamalar (annotation), özel açıklamalar ve yansıma (reflection) (İngilizce).

İpucu: soyut metodu ya da arayüz metodunu gerçeklerken @Override yazın. Adı ya da parametreyi yanlış yazarsanız derleyici hemen uyarır.

Özet

Sık Yapılan Hatalar (1): Soyut Sınıflar

Soyut sınıftan nesne

new Sekil("?") → Sekil is abstract; cannot be instantiated. Somut bir alt sınıf kullanın.

Soyut metodu unutmak

Cember is not abstract and does not override abstract method ciz() in Sekil. Metodu yazın ya da sınıfı abstract yapın.

Soyut metoda gövde

abstract void ciz() { } → abstract methods cannot have a body. Ya abstract ya da gövde.

Sınıfı abstract yapmamak

class Sekil { abstract void ciz(); } → Sekil is not abstract and does not override abstract method ciz() in Sekil.

Çelişen belirteçler

abstract final, private abstract, static abstract → illegal combination of modifiers.

İmzayı yanlış yazmak

double Alan() ya da double alan(int x) yeni bir metottur, soyut alan() hâlâ eksiktir. @Override bunu yakalar.

Özet

Sık Yapılan Hatalar (2): Arayüzler

public'i unutmak

void ciz() { … } → attempting to assign weaker access privileges; was public. Arayüz metodunu public yazın.

Sabiti değiştirmek

GeometrikSekil.KENAR_SAYISI = 5; → cannot assign a value to static final variable KENAR_SAYISI.

İki sınıftan türetmek

class C extends A, B → '{' expected. Tek extends, çok sayıda implements.

Referans türünde olmayan metot

Ucabilir u = new Ordek(); u.yuz(); → cannot find symbol. Uygun türde referans kullanın.

Çakışan default metotlar

class Gorevli inherits unrelated defaults for selam(). Metodu sınıfta geçersiz kılın.

Mühürleme hataları

İzinsiz alt tür: class is not allowed to extend sealed class: Hayvan. İzinli alt türde belirteç yok: sealed, non-sealed or final modifiers expected.

Özet

Örnek Problemler (1): Derlenir mi?

// (a)
abstract class A {
    void f() { System.out.println("f"); }
}
// (b)
class B { abstract void g(); }
// (c)
interface I { int X = 1; }
...  I.X++;
// (d)
interface J { void h(); }
class C implements J { void h() { } }
// (e)
abstract class D { abstract void k(); }
abstract class E extends D { }
  1. Her parça (a)–(e) için: derlenir mi? Derlenmiyorsa hata nedir?
  2. Derlenen parçalardan hangisinin nesnesi new ile oluşturulabilir?
Cevapları göster
  1. (a) Derlenir: soyut metodu olmayan soyut sınıf geçerlidir. (b) Hata: B is not abstract and does not override abstract method g() in B. (c) Hata: cannot assign a value to static final variable X. (d) Hata: h() public değil → weaker access privileges; was public. (e) Derlenir: E de soyut olduğu için k()'yı yazmak zorunda değil.
  2. Hiçbiri: A ve E soyut olduğundan new A(), new E() da derleme hatasıdır.

Özet

Örnek Problemler (2): Çıktı Nedir?

interface Sesli {
    default String ses() { return "..."; }
}
abstract class Hayvan implements Sesli {
    Hayvan() { System.out.print("H "); }
    abstract String ad();
}
class Kedi extends Hayvan {
    Kedi() { System.out.print("K "); }
    String ad() { return "kedi"; }
    public String ses() { return "miyav"; }
}
class Balik extends Hayvan {
    String ad() { return "balık"; }
}
// main içinde:
Hayvan[] d = { new Kedi(), new Balik() };
System.out.println();
for (Hayvan h : d)
    System.out.println(h.ad() + ": " + h.ses());
  1. Program ne yazdırır?
  2. Balik, ses()'i yazmadığı hâlde neden derlenir?
  3. Kedi'deki ses()'ten public silinirse ne olur?
  4. Sesli s = new Balik(); s.ad(); derlenir mi?
Cevapları göster
  1. H K H, sonra kedi: miyav ve balık: .... Kedi oluşurken önce Hayvan yapıcısı (H), sonra Kedi yapıcısı (K); Balik'te yalnızca H.
  2. ses() soyut değil, default: Balik onu miras alır; tek soyut metot ad()'ı yazmıştır.
  3. Derleme hatası: weaker access privileges; was public.
  4. Hayır: cannot find symbol. Sesli türü yalnızca ses()'i bilir.

Özet

Alıştırmalar (1)

  1. Soyut Calisan sınıfı yazın: isim alanı, yapıcı, soyut double maas() ve adı ile maaşı yazdıran bilgi(). Alt sınıflar: TamZamanli (aylık 30000), SaatUcretli (120 saat × 200). Bir Calisan[] dizisinde bilgi() çağırın.
  2. Sekil2B arayüzü: soyut double alan() ve "Alan: " + alan() döndüren default ozet(). Ucgen(6, 4) için ozet() ne döndürür?
  3. interface Hesap { static int topla(int a, int b) … } ve class Basit implements Hesap {}: Hesap.topla(3, 4) ve Basit.topla(3, 4) sonuçları?
Cevaplar / ipuçları
  1. double maas() { return saat * ucret; } gibi; çıktı Ayşe: 30000.0 / Ali: 24000.0.
  2. public double alan() { return taban * yukseklik / 2; } → Alan: 12.0.
  3. 7; ikincisi derlenmez: cannot find symbol (statik arayüz metotları miras geçmez).

Özet

Alıştırmalar (2)

  1. sealed abstract class Sekil permits Kare, Cember yazın; Kare final, Cember non-sealed. Elips extends Cember ve Ucgen extends Sekil derlenir mi?
  2. Soyut sınıf mı, arayüz mü? (a) plakası ve yapıcısı olan Tasit; (b) Ogrenci ve Fatura'nın ortak kaydet() yeteneği; (c) hem Hayvan hem yüzebilen Balik.
  3. class Kare implements GeometrikSekil içinde public void Ciz() { … } yazıldı. Ne olur? Metodun üstüne @Override eklenirse?
Cevaplar / ipuçları
  1. Elips derlenir (Cember non-sealed); Ucgen: class is not allowed to extend sealed class: Sekil.
  2. (a) soyut sınıf (ortak alan + yapıcı); (b) arayüz (ilgisiz sınıflar); (c) class Balik extends Hayvan implements Yuzebilir.
  3. Ciz ≠ ciz (büyük/küçük harf): Kare is not abstract and does not override abstract method ciz() in GeometrikSekil. @Override ile ek olarak method does not override or implement a method from a supertype.

Özet

Kaynaklar

  • Oracle, The Java Tutorials: Abstract Methods and Classes; Interfaces; Default Methods.
  • J. Gosling ve diğ., The Java Language Specification, Java SE 21 Edition (8.1.1.1 abstract Classes, 8.1.1.2 sealed Classes, Bölüm 9: Interfaces).
  • Oracle, JEP 409: Sealed Classes (Java 17) ve JEP 441: Pattern Matching for switch (Java 21).
  • P. Deitel ve H. Deitel, Java: How to Program, 11. baskı, Pearson (Bölüm 10: Object-Oriented Programming: Polymorphism and Interfaces).
  • K. Sierra ve B. Bates, Head First Java, 3. baskı, O'Reilly (Bölüm 8: Interfaces and Abstract Classes).
  • C. S. Horstmann, Core Java, Volume I: Fundamentals, 12. baskı, Pearson (Bölüm 5–6).

Son

Bölüm 4: Soyut Sınıflar ve Arayüzler
Sonraki bölüm: Bölüm 4: Örnek Sorular
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.