← Kursa Dön
📄 Text · 12 min

Default ve Static Metotlar

Neden Default Metotlar?

Java 8 öncesinde interface'lere yeni bir metot eklemek felaket demekti. Düşün: List interface'ine sort() metodu eklemek istiyorsun. Ama List'i implement eden yüzlerce sınıf var — hepsine gidip sort() implement etmek mi lazım? İmkansız.

İşte default keyword'ü bu sorunu çözmek için geldi. Interface'e gövdeli metot eklemeye izin veriyor. Implement eden sınıflar bu metodu override edebilir ama etmek zorunda değil.

Bunu bir cep telefonu güncellemesi gibi düşün. Yeni özellik (karanlık mod) ekleniyor — telefonu kullananların hiçbir şey yapmasına gerek yok, güncelleme gelir, çalışır. Ama isteyen ayarları değiştirebilir. Default metot da bu: güncelleme gelir, mevcut kod kırılmaz.

Default Metot Sözdizimi

interface Greeting {
    // Abstract metot — implement ZORUNLU
    String getName();

    // Default metot — implement opsiyonel
    default void greet() {
        System.out.println("Hello, " + getName() + "!");
    }
}

class EnglishGreeting implements Greeting {
    @Override
    public String getName() { return "World"; }
    // greet() implement etmedik — default versiyonu kullanılır
}

class TurkishGreeting implements Greeting {
    @Override
    public String getName() { return "Dünya"; }

    @Override
    public void greet() { // Override ettik
        System.out.println("Merhaba, " + getName() + "!");
    }
}
Greeting eng = new EnglishGreeting();
eng.greet(); // "Hello, World!"     — default versiyon

Greeting tr = new TurkishGreeting();
tr.greet();  // "Merhaba, Dünya!"   — override edilmiş versiyon

Java Standart Kütüphanesindeki Default Metotlar

Java 8 ile birlikte mevcut interface'lere birçok default metot eklendi:

// List interface'inde
List<String> names = new ArrayList<>(List.of("Zeynep", "Ali", "Burak"));
names.sort(Comparator.naturalOrder()); // sort() bir default metot!
System.out.println(names); // [Ali, Burak, Zeynep]

// Iterable interface'inde
names.forEach(name -> System.out.println(name)); // forEach() default metot!

// Map interface'inde
Map<String, Integer> scores = new HashMap<>();
scores.put("Ali", 85);
scores.getOrDefault("Veli", 0); // getOrDefault() default metot!

Bu metotlar eklenmeden önce mevcut sınıfların hiçbiri kırılmadı. Default metotların gücü bu.

Diamond Problem

Bir sınıf iki interface implement ediyorsa ve her ikisi de aynı isimde default metot içeriyorsa ne olur? Bu diamond problem'dir.

interface A {
    default void hello() {
        System.out.println("Hello from A");
    }
}

interface B {
    default void hello() {
        System.out.println("Hello from B");
    }
}

// Bu DERLEME HATASI verir!
// class C implements A, B { }

Java bu durumda derleme hatası verir ve seni açıkça birini seçmeye zorlar:

class C implements A, B {
    @Override
    public void hello() {
        A.super.hello(); // A'nın versiyonunu kullan
        // veya
        // B.super.hello(); // B'nın versiyonunu kullan
        // veya tamamen kendi versiyonunu yaz
    }
}

InterfaceName.super.method() sözdizimi ile hangi interface'in default metodunu çağıracağını belirtirsin.

class C implements A, B {
    @Override
    public void hello() {
        System.out.println("My own hello!");
        A.super.hello(); // İstersen A'nınkini de çağır
        B.super.hello(); // İstersen B'ninkini de çağır
    }
}
new C().hello();
// My own hello!
// Hello from A
// Hello from B

Diamond Problem Çözüm Kuralları

Java'nın çözüm öncelikleri:

1. Sınıf her zaman kazanır:

interface A {
    default void hello() { System.out.println("A"); }
}

class Parent {
    void hello() { System.out.println("Parent"); }
}

class Child extends Parent implements A {
    // hello() override etmesek bile Parent'ın versiyonu kazanır
}

new Child().hello(); // "Parent" — sınıf interface'i ezer

2. Daha spesifik interface kazanır:

interface A {
    default void hello() { System.out.println("A"); }
}

interface B extends A {
    default void hello() { System.out.println("B"); }
}

class C implements A, B {
    // B daha spesifik (A'yı extend ediyor), B kazanır
}

new C().hello(); // "B"

3. Eşitlerse → derleme hatası, açıkça seç:

İki interface arasında hiyerarşi yoksa ve aynı default metot varsa, sınıf override etmek zorunda.

💡 İpucu: Diamond problem pratikte nadiren karşılaşılan bir durum. Ama çıkarsa kuralları bilmek önemli. Kural basit: "eğer belirsizlik varsa, Java seni zorlar, sen seç."

Static Interface Metotlar

Java 8 ile interface'lere static metot ekleme imkanı da geldi. Bu metotlar interface adıyla çağrılır, implement eden sınıflardan erişilemez.

interface Validator {
    boolean validate(String input);

    // Static factory method
    static Validator emailValidator() {
        return input -> input.contains("@") && input.contains(".");
    }

    static Validator lengthValidator(int min, int max) {
        return input -> input.length() >= min && input.length() <= max;
    }
}
Validator emailCheck = Validator.emailValidator();
System.out.println(emailCheck.validate("ali@mail.com")); // true
System.out.println(emailCheck.validate("ali"));           // false

Validator lengthCheck = Validator.lengthValidator(3, 20);
System.out.println(lengthCheck.validate("Al"));    // false
System.out.println(lengthCheck.validate("Ali"));   // true

Static metotların özellikleri:

  • Interface adıyla çağrılır: Validator.emailValidator()

  • Implement eden sınıftan çağrılamaz

  • Override edilemez

  • Utility metotları ve factory pattern'lar için ideal

Static Metotlar Neden Yararlı?

Eskiden interface ile ilgili utility metotları ayrı bir "helper" sınıfına koyardık:

// Eski stil — yardımcı sınıf
class Collections {
    static <T> List<T> unmodifiableList(List<T> list) { ... }
    static <T> void sort(List<T> list) { ... }
}

// Yeni stil — doğrudan interface'te
interface List<T> {
    static <T> List<T> of(T... elements) { ... }
    static <T> List<T> copyOf(Collection<T> coll) { ... }
}
// Java 9+
List<String> names = List.of("Ali", "Ayşe", "Mehmet");
Map<String, Integer> scores = Map.of("Ali", 85, "Ayşe", 92);

List.of(), Map.of() gibi metotlar interface'teki static metotlardır. Çok temiz ve doğal bir API oluşturuyorlar.

Private Metotlar (Java 9)

Java 9 ile interface'lere private metot ekleme imkanı geldi. Bu, default metotlar arasında kod tekrarını önlemek için.

interface Logger {
    default void logInfo(String message) {
        log("INFO", message);
    }

    default void logError(String message) {
        log("ERROR", message);
    }

    default void logWarning(String message) {
        log("WARNING", message);
    }

    // Private helper — dışarıdan erişilemez
    private void log(String level, String message) {
        System.out.println("[" + level + "] " +
                java.time.LocalDateTime.now() + " - " + message);
    }
}
class MyService implements Logger {
    void doWork() {
        logInfo("Work started");
        // İşlem...
        logInfo("Work completed");
    }
}

log() metodu dışarıdan görünmez. Sadece interface'in kendi default metotları tarafından kullanılır. DRY (Don't Repeat Yourself) prensibini interface seviyesinde uyguluyoruz.

Private static metot da olabilir:

interface DataParser {
    default int parseAge(String text) {
        return parseNumber(text, 0, 150);
    }

    default int parseScore(String text) {
        return parseNumber(text, 0, 100);
    }

    private static int parseNumber(String text, int min, int max) {
        int value = Integer.parseInt(text);
        if (value < min || value > max) {
            throw new IllegalArgumentException("Out of range");
        }
        return value;
    }
}

Interface Metot Türleri Özet

Java 17 itibariyle bir interface şu tür metotları içerebilir:

TürJava VersiyonuÖzellik
abstract1.0Gövdesiz, implement zorunlu
default8Gövdeli, override opsiyonel
static8Interface adıyla çağrılır
private9Sadece interface içinde kullanılır
private static9Static helper metot
interface ModernInterface {
    // Abstract
    void mustImplement();

    // Default
    default void optionalOverride() {
        helperMethod();
        System.out.println("Default implementation");
    }

    // Static
    static ModernInterface create() {
        return new DefaultImpl();
    }

    // Private
    private void helperMethod() {
        System.out.println("Internal helper");
    }

    // Private static
    private static void staticHelper() {
        System.out.println("Static helper");
    }
}

Default Metot Kullanım Kalıpları

1. Backward compatibility — geriye uyumluluk:

interface PaymentGateway {
    void processPayment(double amount);

    // Yeni eklenen — eski sınıflar kırılmaz
    default void processRefund(double amount) {
        throw new UnsupportedOperationException("Refund not supported");
    }
}

2. Opsiyonel davranış:

interface EventListener {
    default void onStart() { }    // Boş default
    default void onProgress(int percent) { }
    default void onComplete() { }
    default void onError(Exception e) { }
}

// Sadece ilgilendiğin event'ları override et
class SimpleListener implements EventListener {
    @Override
    public void onComplete() {
        System.out.println("Done!");
    }
    // Diğerlerini implement etmeye gerek yok
}

3. Mixin benzeri davranış:

interface Timestamped {
    default String getTimestamp() {
        return java.time.Instant.now().toString();
    }
}

interface Identifiable {
    default String generateId() {
        return java.util.UUID.randomUUID().toString();
    }
}

class Event implements Timestamped, Identifiable {
    String name;

    Event(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return generateId() + " [" + getTimestamp() + "] " + name;
    }
}

Gerçekçi Örnek: Koleksiyon İşlemleri

interface Filterable<T> {
    List<T> getItems();

    default List<T> filter(java.util.function.Predicate<T> predicate) {
        List<T> result = new ArrayList<>();
        for (T item : getItems()) {
            if (predicate.test(item)) {
                result.add(item);
            }
        }
        return result;
    }

    default T findFirst(java.util.function.Predicate<T> predicate) {
        for (T item : getItems()) {
            if (predicate.test(item)) {
                return item;
            }
        }
        return null;
    }

    static <T> void printAll(List<T> items) {
        items.forEach(System.out::println);
    }
}
class ProductCatalog implements Filterable<String> {
    private List<String> products;

    ProductCatalog(List<String> products) {
        this.products = products;
    }

    @Override
    public List<String> getItems() {
        return products;
    }
}
ProductCatalog catalog = new ProductCatalog(
    List.of("Laptop", "Mouse", "Keyboard", "Monitor")
);

List<String> result = catalog.filter(p -> p.length() > 5);
Filterable.printAll(result); // Laptop, Keyboard, Monitor

⚠️ Dikkat: Default metotlar interface'leri "mini abstract class"lere dönüştürmez. Interface'in ana amacı hala sözleşme tanımlamak. Default metotları aşırı kullanmak interface'in amacını bulanıklaştırır. Ortak davranış çok fazlaysa abstract class düşün.

Fonksiyonel Interface

Tek abstract metodu olan interface'lere functional interface denir. Lambda expression'larla kullanılabilir.

@FunctionalInterface
interface Transformer {
    String transform(String input);

    // Default ve static metotlar functional interface bozmaz
    default Transformer andThen(Transformer after) {
        return input -> after.transform(this.transform(input));
    }

    static Transformer identity() {
        return input -> input;
    }
}
Transformer upper = String::toUpperCase;
Transformer trim = String::trim;
Transformer combined = trim.andThen(upper);

System.out.println(combined.transform("  hello  ")); // "HELLO"

@FunctionalInterface annotation'ı zorunlu değil ama interface'in functional kalmasını garanti eder — birden fazla abstract metot eklenirse derleme hatası verir.


Özet

  • Default metotlar (Java 8), interface'lere gövdeli metot eklemeye izin verir; mevcut implementasyonları kırmadan interface'i genişletmenin yoludur.

  • Diamond problem iki interface aynı default metodu tanımladığında oluşur; Java açıkça override etmeyi zorunlu kılar, InterfaceName.super.method() ile seçim yapılır.

  • Static interface metotları (Java 8), interface adıyla çağrılır, override edilemez; factory ve utility metotlar için idealdir.

  • Private metotlar (Java 9), interface'in default metotları arasında kod tekrarını önler; dışarıdan erişilemez.

  • Diamond çözüm kuralları: sınıf > daha spesifik interface > belirsizse derleme hatası.

  • Default metotları ölçülü kullan — interface'in amacı sözleşme tanımlamak; çok fazla default metot abstract class gerektiğinin işareti olabilir.