Fonksiyonel Arayüzlerin Detaylı İncelenmesi
Bilindiği gibi bir tane abstract metodu olan arayüzlere fonksiyonel arayüzler(functional interface) denilmektedir. Fonksiyonel arayüz kavramı Java 8 ile birlikte dahil edilen bir konudur. (Mülakat Sorusu!) Fonksiyonel arayüzeler tek bir abstract metod içerdikleri için arayüzün fonksiyonellik yapısı bozulmaması adına default method kavramı da Java 8 ile birlikte eklenmiştir. Default metotlar gövdeli static olmayan metotlardır. Ayrıca bir arayüzün static metotları da olabilmektedir. Fonksiyonel arayüzler Java 8 ile eklenen lambda ifadeleri ve bir sınıfın metot isminin doğrudan verilmesi şeklinde kullanılabilmektedir.
Fonksiyonel Arayüzler ve Kullanımları
package org.csystem;
public class App {
public static void main(String[] args)
{
IX ix = () -> System.out.println("bar with lambda");
ix.foo();
ix.bar();
IX.tar();
}
}
interface IX {
void foo();
default void bar()
{
System.out.println("default bar");
}
static void tar()
{
System.out.println("static tar");
}
}
Fonksiyonel arayüz referans parametreli metotlara lambda ifadesi geçirilebilir.
package org.csystem;
public class App {
public static void main(String[] args)
{
Sample s = new Sample();
s.func(() -> System.out.println("foo"));
}
}
class Sample {
public void func(IX ix)
{
ix.foo();
ix.bar();
IX.tar();
}
}
interface IX {
void foo();
default void bar()
{
System.out.println("default bar");
}
static void tar()
{
System.out.println("static tar");
}
}
Bir arayüzün default metotları içerisinde static yada abstract metotları çağrılabilir. Default metotlar için kullanılacak arayüzün referansını çağırabilmek için bir nesne göstermelidir. Zaten nesne gösteriyor ise abstract metotlarda override edilmiştir.
package org.csystem;
public class App {
public static void main(String[] args)
{
Sample s = new Sample();
s.func(() -> System.out.println("foo"));
}
}
class Sample {
public void func(IX ix)
{
ix.bar();
}
}
interface IX {
void foo();
default void bar()
{
System.out.println("default bar");
this.foo();
}
static void tar()
{
System.out.println("static tar");
}
}
Fonksiyonel Arayüz Referanslarına Metot İsmi ile Değer Verilmesi
Fonksiyonel arayüzlere herhangi bir sınıfın bir metodu atanabilmektedir. Bu durumda o metot arayüzün abstract metoduna karşılık gelmektedir. Şüphesiz ki atanacak metodun arayüzün abstract metoduna uygun yapıda olması gerekir.
Scope Resolution Örneği(Çözünürlük operatörü)
package org.csystem;
public class App {
public static void main(String[] args)
{
IUnaryOperation iu = Math::sqrt;
System.out.println(iu.op(2));
iu = Math::log;
System.out.println(iu.op(2));
}
}
interface IUnaryOperation {
double op(double val);
}
Basit bir komut yorumlayıcı program aşağıdaki proje ile oluşturulabilir:
Sınıf Çalışması
CommandPromt'un HashMap uygulanarak yapılması. Buradaki programda HashMap kullanılarak arama daha etkin hâle getirilmiştir.
Static Olmayan Metotların Fonksiyonel Arayüzlere Atanması
Bilindiği gibi static metotlar herhangi bir referansla çağrılmayan metotlardır. Static olmayan metotlar ise mutlaka bir referansla çağrılması gereken metotlardır. Bu durumda :: sentaks yapısı referans ve metot ismi olacak şekilde aşağıdaki gibi verilebilir.
package org.csystem;
public class App {
public static void main(String[] args)
{
String s = "ankara";
IX ix = s::toUpperCase;
System.out.println(ix.foo());
}
}
interface IX {
String foo();
}
java.util.function Paketi
Java 8 ile birlikte bir takım yararlı fonksiyonel arayüzlerin bildirildiği java.util.function isimli bir paket eklenmiştir. Bu paket içerisinde bulunan arayüzler, Stream APIleri ile kolaylıkla kullanılabilmektedir, bu konu ileride ele alınacaktır. Bu fonksiyonel arayüzler tipik bazı işlemleri kapsamaktadır. Bu arayüzlerden bazıları bu bölümde ele alınacaktır,
Predicate
Predicate arayüzü tek parametreli boolean türünden test isimli bir metoda sahiptir.
Predicate arayüzü ile çift olan sayıların ekrana basılması
package org.csystem;
import java.util.Vector;
import java.util.function.Predicate;
public class App {
public static void main(String[] args)
{
Vector<Integer> vec = new Vector<>();
for (int i = 0; i < 10; ++i)
vec.add(i + 1);
Sample.display(vec, val -> val % 2 == 0);
}
}
class Sample {
public static <T> void display(Vector<T> vec, Predicate<T> pred)
{
for (T val : vec)
if (pred.test(val))
System.out.println(val);
}
}
import java.util.Vector;
import java.util.function.Predicate;
public class App {
public static boolean isEven(Integer val)
{
return val % 2 == 0;
}
public static void main(String[] args)
{
Vector<Integer> vec = new Vector<>();
for (int i = 0; i < 10; ++i)
vec.add(i + 1);
Sample.display(vec, App::isEven);
Sample.display(vec, val -> val % 3 == 0);
}
}
class Sample {
public static <T> void display(Vector<T> vec, Predicate<T> pred)
{
for (T val : vec)
if (pred.test(val))
System.out.println(val);
}
}
Sınıf Çalışması
Predicate
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.function.Predicate;
public class App {
public static boolean isEven(Integer val)
{
return val % 2 == 0;
}
public static void main(String[] args)
{
Vector<Integer> vec = new Vector<>();
for (int i = 1; i <= 10; ++i)
vec.add(i);
LinkedList<Integer> list = new LinkedList<>();
Utils.copyIf(vec, list, val -> val % 2 == 0);
Utils.display(list, val -> true);
}
}
class Utils {
public static <T> void copyIf(List<T> srcList, List<T> destList, Predicate<T> pred)
{
for (T val : srcList)
if (pred.test(val))
destList.add(val);
}
public static <T> void display(List<T> list, Predicate<T> pred)
{
for (T val : list)
if (pred.test(val))
System.out.println(val);
}
}
Sınıf Çalışması
Klavyeden bir yazı isteyiniz, elinizde bulunan String vector içerisinde bu yazıyı içeren Stringleri bir listeye kopyalayan programı yazınız.
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.function.Predicate;
public class App {
public static boolean isEven(Integer val)
{
return val % 2 == 0;
}
public static void main(String[] args)
{
Vector<String> vec = new Vector<>();
vec.add("csd123");
vec.add("c123s5d123");
vec.add("csdcsdcsd");
vec.add("ankara");
vec.add("merhaba");
String str = "csd";
Vector<String> csdVec = new Vector<>();
Utils.copyIf(vec, csdVec, s -> s.contains(str));
Utils.display(csdVec, val -> true);
}
}
class Utils {
public static <T> void copyIf(List<T> srcList, List<T> destList, Predicate<T> pred)
{
for (T val : srcList)
if (pred.test(val))
destList.add(val);
}
public static <T> void display(List<T> list, Predicate<T> pred)
{
for (T val : list)
if (pred.test(val))
System.out.println(val);
}
}
çözünürlük operatörü ile kullanımı
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.function.Predicate;
public class App {
public static boolean isEven(Integer val)
{
return val % 2 == 0;
}
public static void main(String[] args)
{
Vector<String> vec = new Vector<>();
vec.add("çok");
vec.add("güzel");
vec.add("hayır");
vec.add("evet");
vec.add("Bugün");
String str = "Bugün hava çok güzel";
Vector<String> csdVec = new Vector<>();
Utils.copyIf(vec, csdVec, str::contains);
Utils.display(csdVec, val -> true);
}
}
class Utils {
public static <T> void copyIf(List<T> srcList, List<T> destList, Predicate<T> pred)
{
for (T val : srcList)
if (pred.test(val))
destList.add(val);
}
public static <T> void display(List<T> list, Predicate<T> pred)
{
for (T val : list)
if (pred.test(val))
System.out.println(val);
}
}
Function<T, R> Arayüzü
Bu fonksiyonel arayüz, belirli bir girdinin başka bir türden çıkışı için kullanılabilir. apply() abstract metodu ile bu işlem sağlanmaktadır.
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.function.Function;
import java.util.function.Predicate;
public class App {
public static void main(String[] args)
{
Vector<String> vec = new Vector<>();
vec.add("Batuhan Beçik");
vec.add("Burak Öztürk");
vec.add("Yusuf Bıçakçıoğlu");
vec.add("Oğuz Karan");
vec.add("Mert demir");
Vector<Integer> lenVec = new Vector<>();
Utils.copy(vec, lenVec, s -> s.length());
Utils.display(lenVec, val -> true);
}
}
class Utils {
public static <T, R> void copy(List<T> srcList, List<R> destList, Function<T, R> f)
{
for (T val : srcList)
destList.add(f.apply(val));
}
public static <T> void display(List<T> list, Predicate<T> pred)
{
for (T val : list)
if (pred.test(val))
System.out.println(val);
}
}
UnaryOperator<T>
arayüzü Function <T,T>
arayüzünden türetilmiştir.
interface UnaryOperator<T> extends Function<T,T>
Örneğin bir String vektör içerisindeki yazılar büyük harfe çevrilerek aşağıdaki gibi bir copy metoduna eklenebilir.
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
public class App {
public static void main(String[] args)
{
Vector<String> vec = new Vector<>();
vec.add("Batuhan Beçik");
vec.add("Burak Öztürk");
vec.add("Yusuf Bıçakçıoğlu");
vec.add("Oğuz Karan");
vec.add("Mert demir");
Vector<String> lenVec = new Vector<>();
Utils.copy(vec, lenVec, s -> s.toUpperCase());
Utils.display(lenVec, val -> true);
}
}
class Utils {
public static <T> void copy(List<T> srcList, List<T> destList, UnaryOperator<T> f)
{
for (T val : srcList)
destList.add(f.apply(val));
}
public static <T> void display(List<T> list, Predicate<T> pred)
{
for (T val : list)
if (pred.test(val))
System.out.println(val);
}
}
Consumer<T> Arayüzü
Consumer<T>
arayüzü accept(T t) isimli geri dönüş değeri olmayan bir metoda sahiptir. Bu metot ile bir değer parametre olarak alınıp herhangi bir işleme alınabilir.
import java.util.List;
import java.util.Vector;
import java.util.function.Consumer;
public class App {
public static void main(String[] args)
{
Vector<String> vec = new Vector<>();
vec.add("Batuhan Beçik");
vec.add("Burak Öztürk");
vec.add("Yusuf Bıçakçıoğlu");
vec.add("Oğuz Karan");
vec.add("Mert demir");
Utils.walk(vec, System.out::println);
Utils.walk(vec, s -> System.out.println(s.toUpperCase()));
}
}
class Utils {
public static <T> void walk(List<T> list, Consumer<T> consumer)
{
for (T val : list)
consumer.accept(val);
}
}
Anahtar Notlar:
java.util.function paketi içerisindeki birçok arayüzün doğal türler için olan versiyonları da bunlunmaktadır. Bu durumda programcı ayrıca bir açılım yapmak zorunda kalmaz.
BiConsumer<T, U>
Arayüzü
Bu arayüz birden fazla parametre alan ve geri dönüş değeri olmayan 2 parametreli bir accept(T t, U u) metoduna sahiptir.
BiFuntion<T,U,R>
Arayüzü
Bu arayüz T ve U türünden parametreli ve geri dönüş değeri R türünden olan apply(T t, U u, R r)
isimli bir abstract metoda sahiptir.
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
public class App {
public static void main(String[] args)
{
Vector<Integer> vecInt = new Vector<>();
Vector<Long> vecLong = new Vector<>();
Vector<Double> vecDouble = new Vector<>();
vecInt.add(10);
vecInt.add(1000);
vecInt.add(1200);
vecLong.add(10000001L);
vecLong.add(10000002L);
vecLong.add(10000003L);
vecLong.add(10000003L);
Utils.product(vecLong, vecInt, vecDouble, (t, u)->(double)t / u);
Utils.walk(vecDouble, System.out::println);
}
}
class Utils {
public static <T, U, R> void product(List<T> src1, List<U> src2, List<R> dest, BiFunction<T, U, R> bf)
{
int minSize = src1.size() < src2.size() ? src1.size() : src2.size();
for (int i = 0; i < minSize; ++i)
dest.add(bf.apply(src1.get(i), src2.get(i)));
}
public static <T> void walk(List<T> list, Consumer<T> consumer)
{
for (T val : list)
consumer.accept(val);
}
}
Not: Java'da bir sabit int sınırlarını geçerse otomatik long olamaz.
Sınıf çalışması:
Plakalardan oluşan Integer türden bir vector ile şehir isimlerinden oluşan String açılımlı bir vector'u CityInfo isimli bir sınıf türünden vector'de birleştiren programı product isimli metodu kullanarak yazınız.
import java.util.List;
import java.util.Vector;
import java.util.function.BiFunction;
import java.util.function.Consumer;
public class App {
public static void main(String[] args)
{
Vector<String> cities = new Vector<>();
cities.add("ankara");
cities.add("istanbul");
cities.add("izmir");
cities.add("zonguldak");
Vector<Integer> plates = new Vector<>();
plates.add(6);
plates.add(34);
plates.add(35);
plates.add(67);
Vector<CityInfo> cityInfo = new Vector<>();
Utils.product(plates, cities, cityInfo, (plate, city)-> new CityInfo(city, plate));
Utils.walk(cityInfo, System.out::println);
}
}
class CityInfo {
private String m_name;
private int m_plate;
public CityInfo(String name, int plate)
{
super();
m_name = name;
m_plate = plate;
}
public int getPlate()
{
return m_plate;
}
public void setPlate(int plate)
{
m_plate = plate;
}
public String getName()
{
return m_name;
}
public void setName(String name)
{
m_name = name;
}
@Override
public String toString()
{
return m_plate + "-" + m_name;
}
}
class Utils {
public static <T, U, R> void product(List<T> src1, List<U> src2, List<R> dest, BiFunction<T, U, R> bf)
{
int minSize = src1.size() < src2.size() ? src1.size() : src2.size();
for (int i = 0; i < minSize; ++i)
dest.add(bf.apply(src1.get(i), src2.get(i)));
}
public static <T> void walk(List<T> list, Consumer<T> consumer)
{
for (T val : list)
consumer.accept(val);
}
}
Burada şu ifadede
Utils.product(plates, cities, cityInfo, (plate, city)-> new CityInfo(city, plate));
apply() metodu yerine
(plate, city)-> new CityInfo(city, plate)
bu lambda ifadesi geçmiştir.
BinaryOperator<T>
Arayüzü
Bu arayüz BiFunction<T, T, T>
arayüzünden türetilmiştir. Yani apply metodu apply(T t1, T t2)
biçimindedir.
Örneğin:
/*----------------------------------------------------------------------------------------------------------------------
BinaryOperator<T> arayüzü
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;
import java.util.List;
import java.util.Vector;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
public class App {
public static void main(String[] args)
{
BinaryOperator<Integer> biop = (a, b) -> a + b;
System.out.println(biop.apply(10, 20));
biop = (a, b) -> a * b;
System.out.println(biop.apply(10, 20));
}
}
class CityInfo {
private String m_name;
private int m_plate;
public CityInfo(String name, int plate)
{
super();
m_name = name;
m_plate = plate;
}
public int getPlate()
{
return m_plate;
}
public void setPlate(int plate)
{
m_plate = plate;
}
public String getName()
{
return m_name;
}
public void setName(String name)
{
m_name = name;
}
@Override
public String toString()
{
return m_plate + "-" + m_name;
}
}
class Utils {
public static <T, U, R> void product(List<T> src1, List<U> src2, List<R> dest, BiFunction<T, U, R> bf)
{
int minSize = src1.size() < src2.size() ? src1.size() : src2.size();
for (int i = 0; i < minSize; ++i)
dest.add(bf.apply(src1.get(i), src2.get(i)));
}
public static <T> void walk(List<T> list, Consumer<T> consumer)
{
for (T val : list)
consumer.accept(val);
}
}
Sınıf çalışması: Elemanları 1-100 aralığında rastgele belirlenmiş ve boyutları klavyeden girilen iki tane matrisin toplamını başka bir matriste birleştiren matrisler toplanamaz ise exception fırlatan programı fonksiyonel arayüzlerle yazınız.
Örnek Kod
import java.util.List;
import java.util.Random;
import java.util.Vector;
import java.util.function.BiFunction;
import java.util.function.Consumer;
public class App {
public static void main(String[] args)
{
Random r = new Random();
int m = 3, n = 4;
Vector<Vector<Integer>> matrix1 = Utils.getIntegerMatrix(r, m, n, 1, 100);
Utils.displayMatrix(matrix1);
System.out.println("+");
Vector<Vector<Integer>> matrix2 = Utils.getIntegerMatrix(r, m, n, 1, 100);
Utils.displayMatrix(matrix2);
System.out.println("=");
Vector<Vector<Integer>> result = Utils.sum(matrix1, matrix2);
Utils.displayMatrix(result);
}
}
class Utils {
public static Vector<Vector<Integer>> sum(Vector<Vector<Integer>> matrix1, Vector<Vector<Integer>> matrix2)
{
Vector<Vector<Integer>> result = new Vector<>();
Utils.product(matrix1, matrix2, result, (v1, v2) ->
{
Vector<Integer> vec = new Vector<>();
Utils.product(v1, v2, vec, (a, b)-> a + b);
return vec;
});
return result;
}
public static Vector<Vector<Integer>> getIntegerMatrix(Random r, int m, int n, int min, int max)
{
Vector<Vector<Integer>> matrix = new Vector<>();
Vector<Integer> v = null;
for (int i = 0; i < m; ++i) {
v = new Vector<>();
for (int k = 0; k < n; ++k)
v.add(r.nextInt(max - min) + min);
matrix.add(v);
}
return matrix;
}
public static void displayMatrix(Vector<Vector<Integer>> matrix)
{
Utils.walk(matrix, row -> {
Utils.walk(row, val -> System.out.printf("%03d ", val));
System.out.println();
});
}
public static <T, U, R> void product(List<T> src1, List<U> src2, List<R> dest, BiFunction<T, U, R> bf)
{
int minSize = src1.size() < src2.size() ? src1.size() : src2.size();
for (int i = 0; i < minSize; ++i)
dest.add(bf.apply(src1.get(i), src2.get(i)));
}
public static <T> void walk(List<T> list, Consumer<T> consumer)
{
for (T val : list)
consumer.accept(val);
}
}