Eğitim Portalı/Java/Soyutlama: Abstract Sınıflar ve Interface'ler
Java01-java/05-soyutlama-interface

Soyutlama: Abstract Sınıflar ve Interface'ler

Soyutlama, OOP'nin dördüncü temel taşıdır. Fikir basit ama güçlüdür: bir nesneyle çalışırken onun ne yaptığına odaklanır, nasıl yaptığını gizleriz. Bir televizyonun uzaktan kumandasını kullanırken içindeki elektroniği…

Soyutlama: Abstract Sınıflar ve Interface'ler

Soyutlama, OOP'nin dördüncü temel taşıdır. Fikir basit ama güçlüdür: bir nesneyle çalışırken onun ne yaptığına odaklanır, nasıl yaptığını gizleriz. Bir televizyonun uzaktan kumandasını kullanırken içindeki elektroniği bilmene gerek yoktur; sana sadelleştirilmiş bir arayüz sunulur. Java'da bunu iki araçla yaparız: abstract sınıflar ve interface'ler.

Abstract sınıflar

Bazen bir üst sınıf, tek başına anlamlı bir nesne değildir. "Çalışan" derken aslında bir mühendisi veya satışçıyı kastederiz; soyut bir "çalışan" nesnesi oluşturmak mantıksızdır. Böyle durumlarda sınıfı abstract ilan ederiz: ondan doğrudan nesne üretilemez, yalnızca alt sınıfları için bir şablon görevi görür.

Abstract sınıf, içinde hem somut (gövdeli) hem de soyut (gövdesiz) metotlar barındırabilir. Soyut metot, "bu iş yapılacak ama nasıl yapılacağına alt sınıf karar verecek" demektir:

abstract class Calisan {
    public abstract double maasHesapla();   // soyut: alt sınıf doldurur
    public void iseGel() { ... }            // somut: ortak davranış
}

Alt sınıflar soyut metotları doldurmak zorundadır. Örnek 1 (./Ornek1.java) mühendis ve satışçının maaşı farklı hesaplamasını, ortak "işe gel" davranışını üst sınıfta paylaşmasını gösterir.

Interface'ler

Interface, bir sözleşmedir: "bu sözleşmeyi imzalayan her sınıf şu metotları sağlamak zorundadır." Bir yetenek/rol tanımlar. Klasik olarak interface'lerdeki metotlar gövdesizdir:

interface Yuzebilir { void yuz(); }

Bir sınıf bir interface'i implements ile uygular. En önemli farklardan biri: bir sınıf yalnızca tek bir sınıftan türeyebilir ama birden çok interface'i aynı anda uygulayabilir. Böylece "hem yüzen hem yürüyen" gibi çoklu yetenekler modellenebilir:

class Amfibi implements Yuzebilir, Surulebilir { ... }

default ve static metotlar

Java 8'den beri interface'ler gövdeli default metotlar da içerebilir. Bu, uygulayan sınıfları bozmadan arayüze hazır bir davranış eklemeyi sağlar; sınıf isterse ezer, istemezse hazır sürümü kullanır:

interface Surulebilir {
    void yuru();
    default void tanit() { System.out.println("Karada hareket eder."); }
}

Örnek 2 (./Ornek2.java) çoklu interface uygulamasını ve default metodu gösterir.

Abstract sınıf mı, interface mi?

İkisi de soyutlama sağlar ama farklı işler için uygundur:

Abstract sınıfInterface
Amaç"is-a" (bir tür ... olma)"can-do" (bir yeteneğe sahip olma)
ÇokluTek sınıftan mirasBirden çok uygulanabilir
Durum (alan)Tutabilir(Genelde) tutmaz, sabit hariç
Ortak kodBolca paylaşırdefault metotlarla sınırlı

Pratikte ikisi birlikte kullanılır: interface sözleşmeyi tanımlar, abstract sınıf ortak iskeleti sağlar, somut sınıflar boşlukları doldurur. Örnek 3 (./Ornek3.java) bir bildirim sisteminde bunu uygular: Bildirimci interface'i sözleşmeyi, TemelBildirimci abstract sınıfı loglama/biçimlendirme iskeletini verir; e-posta ve SMS kanalları yalnızca kendi gönderim ayrıntısını yazar.

Özet

Soyutlamayla "ne"yi "nasıl"dan ayırdık. Abstract sınıflar ortak iskeleti paylaşıp eksik parçaları alt sınıfa bırakır; interface'ler ise rolleri/yetenekleri tanımlayan sözleşmelerdir ve çoklu uygulanabilir. Bu yaklaşım, kodu gevşek bağlı ve genişletilebilir kılar — ki bu, ileride Spring'de göreceğin "bağımlılığı arayüze ver" felsefesinin de temelidir. Sırada metin işlemenin günlük aracı: String ve StringBuilder.

Kod Örnekleri(3)

Ornek1

çalıştırılabilir
Ornek1.java
1// Ornek1: Abstract sınıf ve abstract metot.
2// Çalıştırma: java Ornek1.java
3public class Ornek1 {
4
5    public static void main(String[] args) {
6        // Abstract sınıftan doğrudan nesne oluşturulamaz:
7        // Calisan c = new Calisan(...);  // <-- derlenmez
8
9        Calisan[] kadro = {
10                new Muhendis("Ada", 30_000),
11                new Satisci("Burak", 20_000, 12_000)
12        };
13
14        for (Calisan c : kadro) {
15            // maasHesapla() üst sınıfta soyut; her alt sınıf kendi kuralını uygular.
16            System.out.printf("%-6s -> aylık maaş: %,.2f TL%n", c.getAd(), c.maasHesapla());
17            c.iseGel(); // ortak (somut) davranış üst sınıfta tanımlı
18        }
19    }
20}
21
22// Abstract sınıf: bazı kısımları hazır (somut), bazıları alt sınıfa bırakılmış (soyut).
23abstract class Calisan {
24    private final String ad;
25    protected final double tabanMaas;
26
27    protected Calisan(String ad, double tabanMaas) {
28        this.ad = ad;
29        this.tabanMaas = tabanMaas;
30    }
31
32    public String getAd() { return ad; }
33
34    // Soyut metot: gövdesi yok; alt sınıflar ZORUNLU olarak doldurur.
35    public abstract double maasHesapla();
36
37    // Somut metot: tüm çalışanlar için ortak.
38    public void iseGel() {
39        System.out.println("   " + ad + " işe geldi.");
40    }
41}
42
43class Muhendis extends Calisan {
44    public Muhendis(String ad, double tabanMaas) { super(ad, tabanMaas); }
45
46    @Override
47    public double maasHesapla() {
48        return tabanMaas * 1.10; // %10 mühendislik zammı
49    }
50}
51
52class Satisci extends Calisan {
53    private final double prim;
54    public Satisci(String ad, double tabanMaas, double prim) {
55        super(ad, tabanMaas);
56        this.prim = prim;
57    }
58
59    @Override
60    public double maasHesapla() {
61        return tabanMaas + prim; // taban + satış primi
62    }
63}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek2

çalıştırılabilir
Ornek2.java
1// Ornek2: Interface, çoklu implementasyon ve default metot.
2// Çalıştırma: java Ornek2.java
3public class Ornek2 {
4
5    public static void main(String[] args) {
6        Amfibi kurbaga = new Amfibi();
7
8        // Bir sınıf birden çok interface'i aynı anda uygulayabilir.
9        kurbaga.yuz();
10        kurbaga.yuru();
11        kurbaga.tanit(); // interface'ten gelen default metot
12
13        System.out.println();
14        Surulebilir araba = new Araba();
15        araba.yuru();
16        araba.tanit();
17    }
18}
19
20// Interface: yalnızca "ne yapılacağını" söyler, "nasıl"ı söylemez (genelde).
21interface Yuzebilir {
22    void yuz(); // soyut: gövdesiz
23}
24
25interface Surulebilir {
26    void yuru();
27
28    // default metot: interface'e gövdeli, hazır bir davranış ekler (Java 8+).
29    // Uygulayan sınıflar isterse ezer, istemezse bu hazır sürümü kullanır.
30    default void tanit() {
31        System.out.println("Bu nesne karada hareket edebilir.");
32    }
33}
34
35// Amfibi HEM yüzer HEM yürür: iki interface'i birden uygular.
36class Amfibi implements Yuzebilir, Surulebilir {
37    @Override public void yuz() { System.out.println("Amfibi suda yüzüyor."); }
38    @Override public void yuru() { System.out.println("Amfibi karada yürüyor."); }
39
40    @Override
41    public void tanit() { // default metodu eziyoruz
42        System.out.println("Ben bir amfibiyim: hem suda hem karada.");
43    }
44}
45
46class Araba implements Surulebilir {
47    @Override public void yuru() { System.out.println("Araba yolda ilerliyor."); }
48    // tanit() ezilmedi -> interface'teki default sürüm çalışır.
49}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek3

çalıştırılabilir
Ornek3.java
1// Ornek3: Abstract sınıf ve interface'i birlikte kullanan gerçekçi senaryo.
2// Bir bildirim sistemi: farklı kanallar (e-posta, SMS) ortak bir arayüzü uygular.
3// Çalıştırma: java Ornek3.java
4import java.util.List;
5
6public class Ornek3 {
7
8    public static void main(String[] args) {
9        // Tüm kanalları tek bir tip (Bildirimci) üzerinden yönetiyoruz.
10        List<Bildirimci> kanallar = List.of(
11                new EmailBildirimci("destek@site.com"),
12                new SmsBildirimci("+90555")
13        );
14
15        String mesaj = "Siparişiniz kargoya verildi.";
16        for (Bildirimci k : kanallar) {
17            k.gonder(mesaj); // log + asıl gönderim (Template Method benzeri akış)
18        }
19    }
20}
21
22// Interface: bir kanalın "gönderebilmesi" sözleşmesi.
23interface Bildirimci {
24    void gonder(String mesaj);
25}
26
27// Abstract sınıf: ortak iskeleti (loglama + biçimlendirme) sağlar,
28// asıl gönderimi alt sınıfa bırakır.
29abstract class TemelBildirimci implements Bildirimci {
30    protected final String hedef;
31    protected TemelBildirimci(String hedef) { this.hedef = hedef; }
32
33    @Override
34    public final void gonder(String mesaj) {
35        System.out.println("[LOG] " + kanalAdi() + " gönderimi hazırlanıyor...");
36        ilet(bicimlendir(mesaj));
37    }
38
39    protected String bicimlendir(String mesaj) {
40        return mesaj.trim();
41    }
42
43    // Alt sınıfların doldurması gereken parçalar:
44    protected abstract String kanalAdi();
45    protected abstract void ilet(String mesaj);
46}
47
48class EmailBildirimci extends TemelBildirimci {
49    EmailBildirimci(String adres) { super(adres); }
50    @Override protected String kanalAdi() { return "E-posta"; }
51    @Override protected void ilet(String mesaj) {
52        System.out.println("E-posta -> " + hedef + " : " + mesaj);
53    }
54}
55
56class SmsBildirimci extends TemelBildirimci {
57    SmsBildirimci(String numara) { super(numara); }
58    @Override protected String kanalAdi() { return "SMS"; }
59    @Override protected void ilet(String mesaj) {
60        // SMS'te kısa tutmak için ilk 30 karakter.
61        String kisa = mesaj.length() > 30 ? mesaj.substring(0, 30) + "..." : mesaj;
62        System.out.println("SMS -> " + hedef + " : " + kisa);
63    }
64}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.