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()
metodudefault
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. - 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