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ş versiyonJava 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 BDiamond 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 ezer2. 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")); // trueStatic 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ür | Java Versiyonu | Özellik |
|---|---|---|
| abstract | 1.0 | Gövdesiz, implement zorunlu |
| default | 8 | Gövdeli, override opsiyonel |
| static | 8 | Interface adıyla çağrılır |
| private | 9 | Sadece interface içinde kullanılır |
| private static | 9 | Static 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.
AI Asistan
Sorularını yanıtlamaya hazır