Java Util Function Paketi ve Lambda Fonksiyonlar

Bu yazımda Java 8 ile birlikte gelen lambda expression ile birlikte, Stream içerisinde yoğun olarak kullanılan java.util.function içerisinde yer alan interfacesleri inceleyeceğiz.

Functional Interface Nedir?

Sadece 1 tane abstract metotu bulunan interface’lere Functional Interface adı verilir. Bu interface Lambda Expression ile birlikte kullanılabilirdir. Bu yüzden farklı bir isme sahip olmuştur. Functional Interface’ler Java 8’in fonksiyonel programlamasını oluşturmaktadır.

Hazır olarak Java içerisinde bulunan Functional Interfacesler olduğu gibi kendimizde sadece bir tane abstract metotu olan bir interface yaratıp bunu lambda expression ile birlikte kullanabiliriz.

Lambda yapısı gereği birden fazla metotu override edemeyecektir. Bu yüzden birden fazla abstract metotu olan interface’sler Lambda ile kullanılamaz.

java.util.function İçindeki Functional Interfacesler.

Belirtilen paket içerisindeki functional interfaceslerimizi 5’e ayırabiliriz.

  • Consumer : Paremetre alarak bir işlem yapar ve sonuç vermez.
  • Supplier: Parametre almaz fakat sonuç verir.
  • Predicate: Parametre alır ve boolean sonuç döndürür.
  • Operator: Parametre alır ve aynı tipde sonuç verrir
  • Function: Parametre alır ve bir sonuç üretir.

Yapısal olarak isimlendirilmeleri

Kaynak:

Java Util Function vs Metot Functionlar

Yazı serisi boyunca çoğu function’a değindim aslında, bunların hepsi birer metot olarak’da yazılabilirdi. Bu yazı boyunca neden Java Util Function paketinin oluşturulduğunu anlatmaya çalışacağım.

Öncelikle ufak bir örnekle nasıl benzerlik gösterdiklerine değinelim.

public static boolean ciftMi(int x) {
    return x % 2 == 0;
}

public static void main(String[] args) {
    Predicate < Integer > ciftMe = (x) - > x % 2 == 0;
}

Bu iki fonksiyon görüldüğü üzere birbirinin aynısı işi yapmaktadır, yani sayının çift olup olmadığını kontrol eder. İlk fonksiyon bir metot olarak yazılmıştır. İkincisi ise lambda kullanılarak functional interface olan Predicate kullanılarak yazılmıştır.

ciftMi(30);
ciftMe.test(30);

Bu iki çağırma yöntemini ele alırsak ilkinin birinciye oranla daha yavaş çalışacağını söyleyebiliriz. Fakat ikincisinin ilkine oranla bir nesne üzerinden çağrıldığına dikkat çekelim. Burada aslında bizim sayının çift olup olmadığını kontrol eden bir nesnemiz var.

Nesneler java dili içerisinde metotlara parametere olarak gönderilebiliyor. Yani ikinci fonksiyonumuz nesne olduğundan dolayı bir metota parametre olarak gönderebiliriz. Buradan çıkartacağımız sonuç, bir metota fonkisyon gönderebiliyor olduğumuzdur.

public static void uygunsaYazdir(Predicate < Integer > fonk, int x) {
    if (fonk.test(x)) {
        System.out.println(x);
    }
}

public static void main(String[] args) {
    Predicate < Integer > ciftMi = (x) - > x % 2 == 0;
    uygunsaYazdir(ciftMi, 3);
    uygunsaYazdir(ciftMi, 6);
    uygunsaYazdir(ciftMi, 7);
    Predicate < Integer > tekmi = ciftMi.negate();
    uygunsaYazdir(tekmi, 1);
    uygunsaYazdir(tekmi, 11);
    uygunsaYazdir(tekmi, 12);

}

Yukarıdaki uygusaYazdir metotumuzu incelediğimizde parametre olarak nesne almasına karşın aslında bu nesne bir fonksiyon. Yani aslında bu metotumuz farklı fonksiyonlara göre farklı şekillerde çalışabilir.

Örnekte verildiği gibi çiftmi fonksiyonu parametre olarak gönderilirse, çift sayıları ekrana yazan bir metot, tekmi gönderilir ise tek sayıları yazan bir metot haline gelicektir.

Bunun gibi fonksiyonlar üzerinde bir çok işlem yapabiliriz çünkü bunlar artık birer nesnelerdir. Fonksiyonel programlamanın amacıda budur aslında, fonksiyonlarımızın sabit olması değil dinamik olmasıdır.

Java Util Function – Function

Parametre alarak sonuç döndüren yapılardır. Lambda ile kullanıldığında apply metotu override edilir. Parametrenin cinsi ve return edilecek olan değerin cinsi farklı veya anı tipte olacak şekilde belirlenebilir.

Function Örnekleri

Function < String, Integer > strUzunluguBul = (x) - > x.length();
System.out.println(strUzunluguBul.apply("dogukan"));

BiFunction < String, Integer, String > tekrarla = (x, y) - > {
    StringBuilder builder = new StringBuilder(x);
    for (int i = 1; i < y; i++) {
        builder.append(",");
        builder.append(x);
    }
    return builder.toString();
};

System.out.println(tekrarla.apply("dogukan", 4));

Function Çeşitleri

Java Util Function – Operator / UnaryOperator

Parametre alarak o parametre ile aynı veri tipinde sonuç üretirler. Lambda ile kullanıldığında applye metotu override edilmektedir. Java Util Function paketi icersindeki Function interfacesine benzerlik göstermektedir.

UnaryOperator<Integer> a= (x)->x*2;
     System.out.println(a.apply(3));
BinaryOperator<Integer> binaryOperator = (x,y)->x*y;
      System.out.println(binaryOperator.apply(3,5));

Operator Çeşitleri

Java Util Function – Supplier

Supplier Ne için Kullanılır?

Supplier parametre almaz ve yalnızca değer döndürür. Lambda ile kullanırken, abstract metotu olan get’i override ederiz. Saati getiren bir fonksiyon, veya rastgele sayı üreten bir fonksiyon supplier için örnek verilebilir.

Supplier Örnekleri

Supplier<String> currentTime = ()-> LocalTime.now().format(DateTimeFormatter.ofPattern("hh:mm"));
System.out.println(currentTime.get());
Supplier<Integer> rndNumber = ()-> new Random().nextInt();
System.out.println(rndNumber.get());
System.out.println(rndNumber.get());

Supplier Çeşitleri

  • BooleanSupplier: Boolean değer döndürür
  • DoubleSupplier: Double değer döndürür.
  • IntSupplier: Integer değer döndürür
  • LongSupplier: Long değer döndürür.

Java Util Function – Consumer

Consumer void fonksiyon gibi düşünülebilir, aldığı paremetre ile sonuç üretmeden işlem yapar. System.out.println kullanarak ekrana yazma Consumer’in kullanılabileceği en iyi örnektir. Lambda expression kullanarak consumerin abstract metotu olan accepti ovveride ederiz.

Consumer<String> ekranaYaz = (x)->System.out.println(x);
ekranaYaz.accept("test");

Consumer içerisinde yer alan andThen metotu kullanarak iki tane Consumer zincirlenebilir ve accept metotu çağrıldığında sıralı olarak çalışırlar.

Consumer<String> ekranaYaz = (x)->System.out.println(x);
ekranaYaz = ekranaYaz.andThen(x->System.out.println("Log: "+x+" ekrana yazildi"));
ekranaYaz.accept("test");

Consumer Çeşitleri

  • BiConsumer: Bir yerine iki parametre alır.
  • DoubleConsumer: Parametre olarak Double alır
  • IntConsumer: Parametre olarak Integer Alır
  • LongConsumer: Parametre olarak Long Alır
  • ObjDoubleConsumer: İki Parametre biri nesne diğeri Double.
  • ObjLongConsumer: İki Parametre biri nesne diğeri Long.
  • ObjIntConsumer: İki Parametre biri nesne diğeri Integer.

Consumer Kullanıldığı Yerler

foearch Metotu Consumer Kullanır.
List<String> liste = new ArrayList<>();
liste.forEach(x->System.out.println(x));
Mapler için foreach BiConsumer Kullanır
Map<Integer,String> map = new HashMap<>();
map.forEach((key,value)->System.out.println("Key: "+key+" Value:"+value));

Java Util Function – Predicate

Predicate Ne için Kullanılır?

Predicate Aldığı paramatereye göre boolean değer döndürmektedir. Filtreleme ve gruplama gibi operasyonlarda kullanılmaktadır. Lambda ile birlikte kullanırken abstract test metotunu override ederiz.

Predicate Metotları

  • test(T t) Lambda ile bu metot override edilir. Parametreye göre boolean sonuç üretmelidir
  • and() İki predicate birleştirilme için kullanılır mantıksal ve ile aynı anlama gelmektedir.
  • or() İki predicate birleştirilmek için kullanılır mantıksal veya ile aynı anlama gelmektedir.
  • negate() Predicate’in ürettiği sonuçun tersini üreten predicate verir, mantıksal not ile aynı anlama gelmektedir.

Predicate Örnekleri

Predicate<Integer> cift = (x)->x%2 ==0;
System.out.println(cift.test(10));

Predicate<String> dIleMiBasliyor = (x)->x.startsWith("d");
Predicate<String> nIleMiBitiyor = (x)->x.endsWith("n");
Predicate<String> dileBaslayipNIleBiten = dIleMiBasliyor.and(nIleMiBitiyor);
System.out.println(dileBaslayipNIleBiten.test("dogukan"));

Predicate<String> dileBaslamasin = dIleMiBasliyor.negate();
Predicate<String> d_ile_baslasin_veya_n_ile_bitsin = dIleMiBasliyor.or(nIleMiBitiyor);

Predicate Çelitleri