İçeriğe geç

Interface ve Abstract Class — Rol mü, İskelet mi?

Orta 8 dk Çok sık karşılaşılır

Önce şunu oku: Polimorfizm ve Dinamik Bağlama — Kararı Kim Veriyor?

30 saniyede özet

Interface bir roldür: bir sınıf aynı anda birkaç rol üstlenebilir. Abstract class ise ortak iskelet ve veri verir, ama bir sınıf yalnızca bir tanesinden türeyebilir. Emin değilsen interface ile başla.

Herkesin uyduğu bir sözleşmeye tek bir madde ekledin. Ertesi sabah telefonun susmuyor.

  1. Bayt: Ödeme sözleşmesine küçücük bir madde ekledim: para iadesi.

  2. Sen: Ne kadar masum bir değişiklik.

  3. Bayt: Ertesi sabah üç ekip birden arıyor!

  4. Bayt: Sözleşmeye madde eklemenin de bir usulü var. Önce şunu soralım: rol mü, soy mu?

Java’da o sözleşme ödeme yöntemlerinin ortak interface’iydi ve eklenen madde refund(). Abstract class’a çevirmek de çare değil: zaten başka bir sınıftan türeyen bir ödeme yöntemi dışarıda kalır.

İkisi de söz verir, farkı ne?

Interfaceabstract classDoğrudan örneklenemeyen, alt sınıflara ortak durum, constructor ve kod sağlayabilen sınıf. Bir sınıf yalnızca bir abstract class'ı genişletebilir.Sözlükte gör →
Bir sınıf kaç tane alabilirİstediği kadarYalnızca bir tane
Örnek durumu (alan)Yok; yalnızca sabitVar
ConstructorYokVar
Gövdeli metotdefault, static, privateHer tür

Rol tanımlamak için ilk tercih interface’tir. Birkaç sınıfın gerçekten ortak verisi ve ortak kodu varsa, interface’in yanına onu uygulayan bir abstract sınıf eklenir. Java’daki List ve AbstractList tam böyle bir ikilidir.

Kafam karıştı, daha basit anlat

Interface bir rol tanımıdır: “şoför araba sürebilir”. Abstract class ise yarı kurulmuş bir iskelettir. Önce rolü yaz, ortak kod gerçekten varsa iskeleti ekle.

Hızlı kontrolBaşlangıç

Interface ile abstract class arasındaki en önemli pratik fark hangisi?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Bir interface'e `int retryCount = 0;` yazdın. Bu nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Bir sözleşmeyi değiştirmek

Başka ekiplerin uyguladığı PaymentMethod interface'ine gövdesiz refund() metodunu eklersen ne olur? Cevabı göster

Bütün uygulamalar derlenmez hâle gelir. Yayınlanmış bir interface’e abstract metot eklemek kırıcı bir değişikliktir.

Soy tektir, roller çoktur; role madde eklemenin iki yolu var.
Adım adım oku
  1. CardPayment yalnızca tek bir sınıftan, PaymentBase'den türeyebilir. Bu onun soyudur.
  2. Ama istediği kadar interface uygular: PaymentMethod, Loggable, Auditable. Bunlar onun rolleridir.
  3. PaymentMethod'a gövdesiz bir refund() eklenirse, onu uygulayan her ekibin kodu derlenmez olur.
  4. Aynı metot default bir gövdeyle eklenirse var olan uygulayıcılar kırılmaz; isteyen kendi sürümünü yazar.

Java 8’den beri interface’teki bir metodun hazır bir gövdesi olabilir: default methodJava 8'den beri bir interface'in gövdesiyle birlikte tanımlayabildiği metot. Mevcut uygulamaları kırmadan sözleşmeyi genişletmeye yarar; örnek durumuna erişemez.Sözlükte gör →. Java ekibi stream() ve forEach’i koleksiyonlara bu sayede, dünyadaki hiçbir kodu kırmadan ekledi.

Default gövde, interface’i uygulayan herkes için mantıklı olmalı. Kendi verisi olamaz; yalnızca interface’in diğer metotlarını çağırabilir.

Aşağıda aynı hikâyenin kodu var: bir bankanın ödeme yöntemleri ve sonradan eklenen iade. Otomatik iadesi olmayan yöntem kırılmaz. Default gövde iadeyi operasyon ekibinin elle işleyeceği kuyruğa yönlendirir.

Derinleş · Ödeme yöntemleri: rol, soy ve sonradan gelen iade 5 dosya · ~96 satır · ilk okumada atlayabilirsin
Proje dosyaları

src/main/java/com/bank/payment/ PaymentMethod.java Rol: her ödeme yöntemi bunu uygular. refund sonradan eklendi; default gövdesi kimseyi kırmaz ve herkes için anlamlıdır: elle inceleme.

src/main/java/com/bank/payment/PaymentMethod.java
public interface PaymentMethod {
String code();
ChargeResult charge(ChargeRequest request);
// Added a year after the first implementations shipped. Abstract, it would have broken
// every team's build; this default is correct for all of them: a person reviews it.
default RefundResult refund(RefundRequest request) {
return RefundResult.manualReview(request, code() + " has no automatic refund");
}
}

src/main/java/com/bank/payment/ AbstractPaymentMethod.java Soy: kart ve cüzdan gibi yöntemlerin ortak verisi ve ortak kodu. Interface'i uygular, üstüne durum ekler.

src/main/java/com/bank/payment/AbstractPaymentMethod.java
// Shared state and shared steps for methods that settle through the bank's own switch.
public abstract class AbstractPaymentMethod implements PaymentMethod {
protected final String merchantId;
private final ChargeAttempts attempts; // idempotency store keyed by request id
protected AbstractPaymentMethod(String merchantId, ChargeAttempts attempts) {
this.merchantId = merchantId;
this.attempts = attempts;
}
// The template: every subclass gets the duplicate-charge guard for free.
@Override
public final ChargeResult charge(ChargeRequest request) {
return attempts.findResult(request.id())
.orElseGet(() -> attempts.record(request.id(), doCharge(request)));
}
protected abstract ChargeResult doCharge(ChargeRequest request);
}

src/main/java/com/bank/payment/ CardPayment.java Kartla ödeme: taban sınıftan türer ve iadeyi kart ağı üzerinden otomatik yapar.

src/main/java/com/bank/payment/CardPayment.java
public final class CardPayment extends AbstractPaymentMethod {
private final CardNetworkClient network;
public CardPayment(String merchantId, ChargeAttempts attempts, CardNetworkClient network) {
super(merchantId, attempts);
this.network = network;
}
@Override
public String code() {
return "CARD";
}
@Override
protected ChargeResult doCharge(ChargeRequest request) {
return network.authorizeAndCapture(merchantId, request.cardToken(), request.amount());
}
// Cards can refund on their own, so this method replaces the default.
@Override
public RefundResult refund(RefundRequest request) {
return network.refund(merchantId, request.originalChargeId(), request.amount());
}
}

src/main/java/com/bank/payment/ LegacyTransfer.java Eski havale: zaten çekirdek bankacılık istemcisinden türüyor, taban sınıfa giremez. Interface sayesinde yine bir ödeme yöntemi; iadesi default gövdeden gelir.

src/main/java/com/bank/payment/LegacyTransfer.java
// Already extends the core-banking client: its one parent is taken.
// It could never join AbstractPaymentMethod, but it can take on the role.
public final class LegacyTransfer extends CoreBankingClient implements PaymentMethod {
public LegacyTransfer(CoreBankingConfig config) {
super(config);
}
@Override
public String code() {
return "LEGACY_TRANSFER";
}
@Override
public ChargeResult charge(ChargeRequest request) {
return postTransfer(request.debtorIban(), request.creditorIban(), request.amount());
}
// No refund() here: the default sends it to manual review, and nothing broke.
}

src/main/java/com/bank/payment/ RefundService.java İade servisi: yalnızca rolü tanır. Elle inceleme gereken iadeyi operasyon kuyruğuna koyar.

src/main/java/com/bank/payment/RefundService.java
@Service
class RefundService {
private final PaymentMethods methods; // looks a method up by its code
private final ManualReviewQueue manualQueue;
RefundService(PaymentMethods methods, ManualReviewQueue manualQueue) {
this.methods = methods;
this.manualQueue = manualQueue;
}
RefundResult refund(RefundRequest request) {
RefundResult result = methods.byCode(request.methodCode()).refund(request);
if (result.needsManualReview()) {
manualQueue.enqueue(request, result.reason()); // back office picks it up
}
return result;
}
}
Kafam karıştı, daha basit anlat

Bir sözleşmeye yeni bir madde eklersen, imzalayan herkes onu yerine getirmek zorunda kalır. Default metot, maddeyi hazır bir cevapla ekler; kimse zorlanmaz.

Hızlı kontrolOrta

Başka ekiplerin uyguladığı yayınlanmış bir interface'e gövdesiz yeni bir metot eklersen ne olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Her ihtiyaç için hangisi daha uygun?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Sınıflandırılmamış

Interface

Bir rol, bir sözleşme; birden fazlası birleştirilebilir

    Abstract class

    Ortak durum, constructor ve iskelet kod

      Kendin gör

      Interface mi, abstract class mı — sözleşme değişince ne olur?

      Tohum 324775
      PaymentMethod.java
      1public interface PaymentMethod {
      2 Receipt pay(Money amount);
      3 Receipt refund(Receipt r); // YENİ, gövdesiz
      4}
      5
      6class CardPayment implements PaymentMethod { … }
      7class WalletPayment implements PaymentMethod { … }
      8class LegacyTransfer extends BankClient /* + PaymentMethod? */ { … }
      Java 21UTF-8LF
      • CardPaymentbekliyor
      • WalletPaymentbekliyor
      • LegacyTransferbekliyor
      Hız
      Adım 0

      Şu an ne oldu?

      Üç uygulama, üç ayrı ekip

      CardPayment ve WalletPayment sade sınıflar. LegacyTransfer zaten BankClient'ı genişletiyor.

      Görevler0/3

      • Tek bir değişiklikle bütün uygulamaları kıraçık

        İpucu

        Yayınlanmış bir sözleşmeye gövdesiz bir metot eklersen?

      • Hiçbir uygulamayı kırmadan yeni bir metot ekleaçık

        İpucu

        Java 8'den beri interface metotlarının bir gövdesi olabilir.

      • Bir uygulamanın sözleşmeye hiç katılamadığı bir tasarım seçaçık

        İpucu

        Zaten başka bir sınıfı genişleten biri, ikinci bir sınıfı genişletebilir mi?

      Olay günlüğü (0)

      Henüz olay yok. Oynat veya adımla.

      1. Varsayılanla oynat. Gövdesiz refund(): üç uygulama da kırıldı.
      2. “interface + default metot” seç. Hiçbiri kırılmadı.
      3. “abstract class” seç. Yeni metot sorunsuz, ama LegacyTransfer baştan beri dışarıda.
      4. Değişikliği “ortak durum” yap. Interface’ler durum taşıyamıyor.
      5. “İkinci rol” seç. Interface’le herkes iki rolü birden üstleniyor.
      Hızlı kontrolOrta

      Her servis sınıfı için tek uygulamalı bir interface yazılıyor: `OrderService` + `OrderServiceImpl`. Bu ne zaman gereksizdir?

      Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

      Satır satır: iki default çakışınca

      Java belirsizliği senin yerine çözmez

      Device.java
      1interface Scanner { default String name() { return "tarayıcı"; } }
      2interface Printer { default String name() { return "yazıcı"; } }
      3
      şu an çalışan satırclass OfficeDevice implements Scanner, Printer {
      5 @Override
      6 public String name() {
      7 return Printer.super.name() + " + " + Scanner.super.name();
      8 }
      9}

      Debug

      Adım 1/4

      derleyici OfficeDevice name()'i iki interface'ten birden devralıyor. İkisi de default.

      adaylar
      = Scanner.name, Printer.name
      Java 21UTF-8LF4:1

      Sol/sağ ok tuşlarıyla da gezebilirsin.

      İki interface aynı isimde iki farklı default gövde getirirse Java hangisini seçeceğini bilemez. Buna diamond problemBir sınıfın, aynı imzalı default metodu sağlayan iki interface'i birden uygulaması. Java bunu belirsiz sayar ve sınıfın metodu açıkça override etmesini ister.Sözlükte gör → denir. Kural basit: sınıfın kendi yazdığı kazanır; çatışma varsa sınıf metodu yazıp kararı kendisi verir.

      Kafam karıştı, daha basit anlat

      İki arkadaş sana aynı soruya farklı cevap verdiyse, hangisini seçeceğine sen karar verirsin. Java da aynısını ister: sınıf, çakışan metodu kendisi yazar.

      Hızlı kontrolOrta

      Program ne yazdırır?

      Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.
      Priority.java
      1interface Greeter { default String hello() { return "interface"; } }
      2class Base { public String hello() { return "class"; } }
      3class Impl extends Base implements Greeter { }
      4
      5System.out.println(new Impl().hello());
      Java 21UTF-8LF

      Tuzaklar

      Alışkanlıkla interface açmak. OrderService ve OrderServiceImpl ikilisinin ikinci bir uygulaması yoksa, interface yalnızca fazladan bir dosya ve bir atlama ekler.

      Default metoda iş mantığı doldurmak. Interface gizlice bir taban sınıfa dönüşür ve kalıtımın kırılganlık sorunu da onunla birlikte gelir.

      Interface’te “alan” sanmak. Interface’e yazılan int retryCount = 0; herkesin paylaştığı bir sabittir, her sınıfa ait bir sayaç değil.

      Record’u bir sınıftan türetmeye çalışmak. Record’lar başka bir sınıftan türeyemez. Kapalı bir tip ailesi istiyorsan sealed interface kullan.

      Hızlı kontrolOrta

      Bu sınıf derlenmiyor. Hangi satır sorunun kaynağı ve nasıl düzeltilir?

      Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

      Hatalı satıra dokun, sonra kontrol et.

      Report.java
      Java 21UTF-8LF

      Kendini sına

      Şimşek turu1/5

      Bir sınıf birden fazla interface uygulayabilir.

      Soru 1/2İleri

      Bir interface'e iş mantığı içeren büyük default metotlar eklenmeye başlandı; metotlar birbirini ve getter'ları çağırıyor. Risk ne?

      Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

      Aklında kalacak üç şey

      1. 1 Bir sınıf birçok interface uygulayabilir ama yalnızca tek bir sınıftan türeyebilir. Rol tanımlarken ilk tercih interface'tir.
      2. 2 Kullanılan bir interface'e gövdesiz yeni metot eklemek onu uygulayan herkesi kırar. Default metot kırmadan genişletir ama veri tutamaz.
      3. 3 İki interface aynı default metodu getirirse Java seçim yapmaz; sınıf metodu kendisi yazar ve hangisini istediğini açıkça söyler.
      Sonraki kapı Bir record oluşturdun ve hiç dokunmadın, ama içindeki liste bir gün değişmiş çıktı. Kim değiştirdi? Değişmez Sınıf Tasarlamak — Bir Kez Kur, Hep Güven · 9 dk

      3 kart sonraki derste seni bekliyor

      0/5 kart bu dersten toplandı

      Bu dersin üstüne kurulanlar

      Bunlar bu dersi temel alıyor; hazır olduğunda devam edebilirsin.