Skip to content

Latest commit

 

History

History
276 lines (204 loc) · 8.57 KB

chapter-09.asc

File metadata and controls

276 lines (204 loc) · 8.57 KB

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

  • Virtural 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.

Doğru bir kullanım örneği
public interface Arac {
    void gazla(); (1)
}
  1. Soyut, gövdesiz metod, doğru kullanım.

Yanlış bir kullanım örneği
public interface Arac {
    void gazla() {
     // bla bla bla
    }; (1)
}
  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 (1)

        System.out.println("Araç:  çalışıyor..");

    }

    void dur(); // Soyut metod (2)
}
  1. numaralı kısımdaki gazla() metodu default anahtar 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.

  2. 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.

Arac türünden Minibus sınıfı
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)
  1. Arac içindeki gazla() varsayılan metodu koşturuluyor.

  2. 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.

Aynı isimde varsayılan metodlara sahip iki arayüz
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.

Çakışma durumu örneği
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.

Çakışma durumu hata mesajı
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.

Çakışma durumunun giderilmesi - 1
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.

Çakışma durumunun giderilmesi - 2
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)
    }
}
  1. Arac arayüzünün gazla() metodunu koşturur

  2. 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<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }

    ...

    @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, 0);
    }

    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }

    default Stream<E> 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<T> 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..