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ıf | Interface | |
|---|---|---|
| Amaç | "is-a" (bir tür ... olma) | "can-do" (bir yeteneğe sahip olma) |
| Çoklu | Tek sınıftan miras | Birden çok uygulanabilir |
| Durum (alan) | Tutabilir | (Genelde) tutmaz, sabit hariç |
| Ortak kod | Bolca paylaşır | default 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ılabilir1// 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}Ornek2
çalıştırılabilir1// 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}Ornek3
çalıştırılabilir1// 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}