Selamlar, bu yazımda sizlere Java8 ile gelen Functional Interface özelliğini anlatmaya çalışacağım.
Interface Nedir?
Peki, interface ne demek? Java programlama dilinde ve diğer birçok nesne yönelimli programlama dilinde kullanılan “interface” (arayüz) terimi, programlama dünyasında önemli bir rol oynar. Bir arayüz, bir sınıfın veya nesnenin belirli metotları veya davranışları uygulamasını sağlayan bir sözleşme türüdür. Başka bir deyişle, bir sınıf, bir arayüzü uyguladığında, o arayüzde tanımlanan tüm metotları gerçeklemek zorundadır.
Arayüzler, kodun soyutlanmasını ve modülerliğini artırmanın yanı sıra çoklu kalıtımı simüle etmek için kullanılır. Yani bir sınıf yalnızca bir ana sınıfı miras alabilirken, birden fazla arayüzü uygulayabilir. Bu, Java gibi dillerde çoklu kalıtımın eksikliğini telafi etmek için kullanılır.
Bir arayüz, içinde hiçbir somut (gerçek) metot olmayan soyut metotların bir koleksiyonunu içerir. Bu soyut metotlar, arayüzü uygulayan sınıflar tarafından gerçekleştirilmelidir. Bu sayede, bir arayüzü uygulayan sınıflar arasında belirli bir davranışın sağlanması garanti edilir. Örneğin, bir “SesliArayuz” arayüzü, “sesliKomutAl” ve “sesiTanımla” gibi metotları içerebilir. Bu arayüzü uygulayan sınıflar, bu metotları kendi gereksinimlerine göre uygularlar.
Ayrıca, Java 8’den itibaren fonksiyonel programlama özellikleri de eklenmiştir ve bu nedenle “Functional Interface” (fonksiyonel arayüz) terimi ortaya çıkmıştır. Functional Interface, yalnızca bir tek soyut metodu (genellikle bir lambda ifadesi olarak kullanılabilir) içeren arayüzleri ifade eder. Bu tür arayüzler, lambda ifadeleri ve fonksiyonel programlama teknikleriyle birlikte kullanılarak işlevsel programlama paradigmasını desteklerler.
Özetle, “interface” terimi, programlamada bir sınıfın veya nesnenin sahip olması gereken metotları tanımlayan bir şablondur ve Java’da çoklu kalıtımın eksikliğini telafi etmek için kullanılır. “Functional Interface” ise yalnızca bir tek soyut metodu olan arayüzleri ifade eder ve modern Java’da fonksiyonel programlamaya olanak tanır.
Functional Interface Nedir?
Java 8 ile gelen ve içerisinde yalnızca bir adet abstact metod barındıran interface’lerdir. Eğer interface’imiz içerisinde yalnızca tek bir metod bulunuyor ve bu metodda abstract ise o zaman buna Functional Interface diyebiliriz. Ancak içeriisnde default metod barındırması bizler için sorun teşkil etmemekte. Kısaca tekrar tanım yapacak olursak içerisinde sadece ve sadece 1 adet abstract metod ve istediği kadar default metod barındıran interface’lere biz Functional Interface diyoruz. Functional Interface’ler Lamda kullanımı sağlamak amacıyla da tanımlanmaktadır. Bu interface’lerimizi tanımlarken @FunctionalInterface
olarak belirtmemiz gerekmekte. Ancak belirtmesek dahi Java ile kullanım sağlayabiliriz. Gelin bir tane tanımlayalım.
@FunctionalInterface public interface Greeting<T> { abstract void get(T t); default Greeting<T> after(Greeting<T> obj) { return (T t) -> { this.get(t); obj.get(t); }; } }
Şimdi burada ne yaptığımdan biraz bahsedeceğim. T tipinde bir Interface tanımladım ve içerisine 1 adet abstract, 1 adet default metod tanımlaması yaptım. T tipinde olması demek içerisine vereceğim tipe göre işlem yapmak istediğimizi ifade ediyor. Mesela List tanımı yaparken nasıl List<String> diyoruz. Aynı burada ki mantık ile işliyor. Bkz :
public interface List<E> extends Collection<E>
Aslında Java’da çoğumuzun kullandığı bir Functional Interface var. Thread tanımı yaparken kullanmışsınızdır mesela.Şimdi hatırlayacaksınız.
@FunctionalInterface public interface Runnable { public abstract void run(); }
Thread’ler için kullandığımız Runnable Interface’i de bir Functional Interface’dir. Kendi Functional Interface’imizin örneklemesinden önce gelin bunu da bir örnekleyelim. Lambda olmadan kullanımını ve lambda ile olan kullanımını görelim.
Lambda olmadan kullanımı:
new Thread(new Runnable() { @Override public void run() { System.out.println("Thread çalışıyor!"); } }).start();
Lambda ile kullanımı:
new Thread(() -> System.out.println("Thread çalışıyor!")).start();
Aradaki uçurumu gösteren sihirli değneğimiz olan lambda işte böyle bir şey.
Şimdi de kendi yazdığımıza bir bakalım. Eskiden olsa nasıl yapardık şeklinde bir örnekleme gerçekleştirelim ve nasıl duracağına bakalım.
Greeting<String> greeting = new Greeting<String>() { @Override public void get(String s) { // kodlar } };
Bu şekilde bir tanımlama göze hoş gelmediği gibi okuması da göz yorucu. Bir de lamba ile deneyelim. Yapacağı işlemde parametresini yazdırmak olsun.
Greeting<String> greeting = s -> System.out.println(s);
Böyle daha güzel duruyor değil mi? Ama bunun daha güzel yöntemi daha var: Method Reference kullanmak. Bir de ona bakalım.
Greeting<String> greeting = System.out::println;
Bu daha da güzel oldu sanki ne dersiniz? Şimdi oluşturduğum obje ile default metodumuz olan get metodunu kullanacağım ve bu metoda vereceğim parametre ile abstract metodum için yaptığım tanımlada ki işlem neyse onu gerçekleyecek. Ben burada sadece gelen String ifadeyi yazdırsın diyorum. Ancak bu String türünde tanımlı olmak zorunda değil. Örneğin türünü List yaparak, metod içeriğini gelen listeyi temizlemesi için kullanabilirdik. Bu tarz uygulamarı da deneyerek pratik yapabilirsiniz.
Greeting<String> greeting = System.out::println; greeting.get("codingbytime.com");
Çıktımızda doğal olarak codingbytime.com olarak karşımıza gelmekte.