
Java 8 – Default Methods
Java 8 ile beraber varsayılan metod özelliği bir dil özelliği olarak Java’ya katıldı. Varsayılan metodların literatürde birçok farklı isim ile anılmaktadır. Bunlar;
- Default method
 - Defender method
 - Virtual extension method
 
Java 8 evvelinde arayüz bileşenlerinde dilin tasarımı açısından sadece soyut metodlar bulunabilmekteydi. Somut yani gövdeli metodlar bulunamamaktaydı. Aşağıda doğru ve yanlış kullanımlara birer örnek görmekteyiz.
public interface Arac {
    void gazla(); (1)
}
- Soyut, gövdesiz metod, doğru kullanım.
 
public interface Arac {
    void gazla() {
     // bla bla bla
    }; (1)
}
- Somut, gövdeli metod, yanlış kullanım.
 
Varsayılan Metoda Giriş
Java 8 ile birlikte bir arayüz bileşeninde bir yada daha fazla sayıda defender method tanımlanabilmektedir. Varsayılan metodlar default anahtar kelimesiyle tanımlanmaktadır. Örneğin;
public interface Arac {
    default void gazla(){ // Defender method
        System.out.println("Araç:  çalışıyor..");
    }
    void dur(); // Soyut metod
}
- numaralı kısımdaki 
gazla()metodudefaultanahtar ifadesi aracılığıyla bir varsayılan metoda dönüştürülmüştür. Varsayılan metodlar arayüzlere iş mantığı yerleştirmeye müsade eden özel metodlardır. - numaralı kısımda ise olağan biçimde gövdesiz bir metod bulunmaktadır.
 
Varsayılan metoda sahip bir arayüzden türeyen alt sınıflar, arayüzün sahip olduğu tüm defender metodları tüketebilmektedir.
public class Minibus implements Arac {
    @Override
    public void dur() {
        System.out.println("Minibüs duruyor..");
    }
}
Örneğin yukarıda yer alan Minibus sınıfı Arac`arayüzü türünden bir sınıftır. Bu sebeple, Minibus sınıfı türünden nesneler `Arac arayüzü içerisindeki gazla() metodunu koşturabilecektir.
Minibus minibus=new Minibus();
minibus.gazla(); (1)
minibus.dur(); (2)
- Arac içindeki 
gazla()varsayılan metodu koşturuluyor. - Minibus içindeki 
dur()metodu koşturuluyor. 
Araç: çalışıyor.. Minibüs duruyor..
Yukarıda görüldüğü üzere, normalde Minibus sınıfı içerisinde gazla() metodu bulunmamasına rağmen, Arac arayüzü içindeki defender metodu koşturabildi.
Varsayılan metodlarda çakışma
Eğer bir Java sınıfı, aynı isimde varsayılan metoda sahip birden fazla arayüzü uygularsa, derleme zamanında hata ile karşılaşılır.
public interface Arac { (1)
    default void gazla(){
        System.out.println("Araç:  çalışıyor..");
    }
    void dur();
}
public interface Tasit { (2)
    default void gazla(){
        System.out.println("Taşıt: çalışıyor..");
    }
}
Örneğin (1) numarada Arac, (2) numarada da Tasit arayüzleri gazla() adında varsayılan metodlara sahiptir.
Şimdi bu iki türü birden uygulayan bir Otobus sınıfı yazalım.
public class Otobus implements Arac, Tasit {
    @Override
    public void dur() {
        System.out.println("Araç duruyor..");
    }
}
Otobus sınıfı bu haliyle derlendiğinde aşağıdaki derleme hatası alınacaktır.
Error:(6, 8) java: class com.kodcu.def.Otobus inherits unrelated defaults for gazla() from types com.kodcu.def.Arac and com.kodcu.def.Tasit
Çünkü ortada  Otobus sınıfının hangi gazla() metodunu koşturacağına dair bir ikilem vardır. JVM ikilem durumlarını hiç sevmez, ona bir seçim şansız sunmalıyız.  Bu çakışma durumu, varsayılan metodu Otobus sınıfı içinde yeniden düzenlenerek (@Override ederek) giderilebilmektedir.
public class Otobus implements Arac, Tasit {
    @Override
    public void dur() {
        System.out.println("Araç duruyor..");
    }
    @Override
    public void gazla() {
        System.out.println("Otobüs çalışıyor..");
    }
}
Otobus sınıfına gazla() metodu ekleyerek yeniden düzenlendiğinde artık çakışma durumu giderilmiş durumdadır. Sınıf bu haliyle Otobüs çalışıyor.. mesajını çıktılayacaktır.
Fakat burada farklı bir ihtiyaç daha göze batmaktadır. Bu durumda Arac veya Tasit arayüzleri içindeki çakışan gazla() metodları nasıl alt sınıflarda kullanılabilir?
İşte bu noktada <arayüz-adı>.super.<metod-adı>() biçimi ile arayüzlerdeki varsayılan metodlar çakışma olmadan koşturulabilmektedir.
public class Otobus implements Arac, Tasit {
    @Override
    public void dur() {
        System.out.println("Araç duruyor..");
    }
    @Override
    public void gazla() {
        Arac.super.gazla(); (1)
            /* veya */
        Tasit.super.gazla(); (2)
    }
}
- Arac arayüzünün 
gazla()metodunu koşturur - Tasit arayüzünün 
gazla()metodunu koşturur 
Varsayılan metodlar ve Fonksiyonel arayüzler
Fonksiyonel arayüzler, tek bir gövdesiz metoda sahip özel arayüzlerdir. Eğer bir Java arayüzünün içinde birden fazla sayıda soyut metod varsa, bu arayüzler fonksiyonel arayüz olamamaktadır. Fonksiyonel arayüzlerin en önemli özelliği, Lambda ifadesi olarak temsil edilebilmesidir.
Arayüzler içinde tanımlanan varsayılan metodlar ise, bir arayüzün fonksiyonel olabilmesini etkilememektedir. Çünkü yazılan Lambda deyimleri, arayüz içindeki tek bir soyut metoda odaklı olarak dönüştürülmektedir.
public interface Arac {
    default void gazla(){
        System.out.println("Araç:  çalışıyor..");
    }
    void dur();
}
Örneğin yukarıdaki Arac arayüzünü fonksiyonel arayüz olabilirliği açısından değerlendirelim. Arac sınıfının fonksiyonel arayüz olabilmesi için tek bir soyut metoda sahip olması gerekmektedir. Arac arayüzü içindeki dur() metodu soyuti gövdesiz bir metod olduğu için bir fonksiyonel arayüzdür. gazla() metodu ise bir varsayılan metod olduğundan fonksiyonel olabilirliğe bir etkisi yoktur. Bu noktada, Arac arayüzü bir fonksiyonel arayüz olduğundan Lambda deyimi olarak yazılabilecektir. Tabi ki Lambda deyimi, dur() isimli soyut metod dikkate alınarak yazılmalıdır.
Arac otobus = ()-> System.out.println("Otobüs duruyor.."); (1)
otobus.gazla();
otobus.dur();
Araç: çalışıyor.. Otobüs duruyor..
Yukarıda (1) numarada yazılı Lambda deyimi, dur() metoduna karşılık olarak tanımlanmıştır. Bu sebeple Arac arayüzü türünden bir nesne oluşturmaktadır.
Varsayılan metodlar ve JDK
JDK 1.8 içerisinde bazı noktalarda varsayılan metodlar kullanılmaktadır. java.util.Collection arayüzünde bunu fazlaca görmekteyiz.
public interface Collection<E> extends Iterable<E> {
    ...
    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }
    ...
    @Override
    default Spliterator spliterator() {
        return Spliterators.spliterator(this, 0);
    }
    default Stream stream() {
        return StreamSupport.stream(spliterator(), false);
    }
    default Stream parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }
}
Collection sınıfı içindeki removeIf, stream, parallelStream ve spliterator metodları varsayılan metodlardır. Bu sebeple Collection türünden tüm nesneler bu varsayılan metodları miras alarak tüketebilmektedir.
Collection arayüzünde olduğu gibi Iterable arayüzünde de varsayılan metod bulunduğunu görebiliyoruz.
@FunctionalInterface
public interface Iterable<T> {
    Iterator iterator();
    default void forEach(Consumer<? super T> action) {
    	Objects.requireNonNull(action);
    	for (T t : this) {
        	action.accept(t);
    	}
    }
}
Iterable#forEach varsayılan metodu sayesinde Iterable türünden tüm veri tipleri, bu metodu ortak olarak tüketebiliyor.
Tekrar görüşmek dileğiyle..
Tag:backend



