Java Lambda İfadeleri (Örneklerle)

Bu yazıda örnekler yardımıyla Java lambda ifadesi ve lambda ifadesinin fonksiyonel arayüzler, jenerik fonksiyonel arayüz ve stream API ile kullanımını öğreneceğiz.

Lambda ifadesi ilk kez Java 8'de tanıtıldı. Ana amacı dilin ifade gücünü artırmaktır.

Ancak lambdalara girmeden önce, öncelikle işlevsel arayüzleri anlamamız gerekir.

İşlevsel Arayüz nedir?

Bir Java arabirimi bir ve yalnızca bir soyut yöntem içeriyorsa, o zaman işlevsel arabirim olarak adlandırılır. Bu tek yöntem, arayüzün amaçlanan amacını belirtir.

Örneğin, Runnablepaketin arayüzü java.lang; işlevsel bir arayüzdür çünkü sadece bir metodu oluşturur, yani run().

Örnek 1: Java'da bir İşlevsel Arayüz Tanımlayın

 import java.lang.FunctionalInterface; @FunctionalInterface public interface MyInterface( // the single abstract method double getValue(); )

Yukarıdaki örnekte, MyInterface arabiriminin yalnızca bir getValue () soyut yöntemi vardır. Dolayısıyla işlevsel bir arayüzdür.

Burada ek açıklamayı kullandık @FunctionalInterface. Açıklama, Java derleyicisini arabirimin işlevsel bir arabirim olduğunu göstermeye zorlar. Bu nedenle birden fazla soyut yönteme izin vermez. Ancak zorunlu değildir.

Java 7'de, işlevsel arayüzler Tek Soyut Yöntemler veya SAM türü olarak kabul edildi . SAM'ler genellikle Java 7'deki Anonim Sınıflar ile uygulanmıştır.

Örnek 2: SAM'ı java'daki anonim sınıflarla uygulama

 public class FunctionInterfaceTest ( public static void main(String() args) ( // anonymous class new Thread(new Runnable() ( @Override public void run() ( System.out.println("I just implemented the Runnable Functional Interface."); ) )).start(); ) )

Çıktı :

 Runnable Functional Interface'i yeni uyguladım.

Burada anonim bir sınıfı bir yönteme geçirebiliriz. Bu, Java 7'de daha az kod içeren programların yazılmasına yardımcı olur. Ancak, sözdizimi hala zordu ve çok sayıda ekstra kod satırı gerekiyordu.

Java 8, SAM'lerin gücünü bir adım daha ileri giderek genişletti. İşlevsel bir arabirimin yalnızca bir yöntemi olduğunu bildiğimizden, onu bir argüman olarak iletirken o yöntemin adını tanımlamaya gerek olmamalıdır. Lambda ifadesi tam olarak bunu yapmamızı sağlar.

Lambda ifadelerine giriş

Lambda ifadesi esasen anonim veya isimsiz bir yöntemdir. Lambda ifadesi kendi kendine çalışmaz. Bunun yerine, işlevsel bir arayüz tarafından tanımlanan bir yöntemi uygulamak için kullanılır.

Java'da lambda ifadesi nasıl tanımlanır?

Java'da lambda ifadesini şu şekilde tanımlayabiliriz.

 (parameter list) -> lambda body

Kullanılan yeni operatör ( ->), ok operatörü veya lambda operatörü olarak bilinir. Sözdizimi şu anda net olmayabilir. Bazı örnekleri inceleyelim,

Diyelim ki, şöyle bir yöntemimiz var:

 double getPiValue() ( return 3.1415; )

Bu yöntemi lambda ifadesini kullanarak şöyle yazabiliriz:

 () -> 3.1415

Burada yöntemin herhangi bir parametresi yoktur. Bu nedenle, operatörün sol tarafı boş bir parametre içerir. Sağ taraf, lambda ifadesinin eylemini belirten lambda gövdesidir. Bu durumda 3.1415 değerini döndürür.

Lambda Gövde Türleri

Java'da lambda gövdesi iki tiptir.

1. Tek bir ifadeye sahip bir gövde

 () -> System.out.println("Lambdas are great");

Bu tür lambda gövdesi, ifade gövdesi olarak bilinir.

2. Bir kod bloğundan oluşan bir gövde.

 () -> ( double pi = 3.1415; return pi; );

Lambda gövdesinin bu türü bir blok gövde olarak bilinir. Blok gövdesi, lambda gövdesinin çoklu ifadeler içermesine izin verir. Bu ifadeler kaşlı ayraçların içine alınır ve küme parantezlerinden sonra noktalı virgül eklemeniz gerekir.

Not : Blok gövdesi için, gövde bir değer döndürürse bir return ifadesine sahip olabilirsiniz. Bununla birlikte, ifade gövdesi bir dönüş ifadesi gerektirmez.

Örnek 3: Lambda İfadesi

Lambda ifadesini kullanarak Pi'nin değerini döndüren bir Java programı yazalım.

Daha önce belirtildiği gibi, bir lambda ifadesi kendi başına çalıştırılmaz. Aksine, fonksiyonel arayüz tarafından tanımlanan soyut yöntemin uygulanmasını oluşturur.

Bu yüzden önce işlevsel bir arayüz tanımlamamız gerekiyor.

 import java.lang.FunctionalInterface; // this is functional interface @FunctionalInterface interface MyInterface( // abstract method double getPiValue(); ) public class Main ( public static void main( String() args ) ( // declare a reference to MyInterface MyInterface ref; // lambda expression ref = () -> 3.1415; System.out.println("Value of Pi = " + ref.getPiValue()); ) )

Çıktı :

 Pi Değeri = 3.1415

Yukarıdaki örnekte,

  • MyInterface adında işlevsel bir arayüz oluşturduk. Adlı tek bir soyut yöntem içerir.getPiValue()
  • Main sınıfının içinde, MyInterface'e bir referans bildirdik. Bir arayüzün referansını bildirebileceğimizi, ancak bir arayüzü başlatamayacağımızı unutmayın. Yani,
     // it will throw an error MyInterface ref = new myInterface(); // it is valid MyInterface ref;
  • Daha sonra referansa bir lambda ifadesi atadık.
     ref = () -> 3.1415;
  • Son olarak, getPiValue()referans arayüzünü kullanarak yöntemi çağırıyoruz . Ne zaman
     System.out.println("Value of Pi = " + ref.getPiValue());

Parametreli Lambda İfadeleri

Şimdiye kadar herhangi bir parametre olmadan lambda ifadeleri oluşturduk. Bununla birlikte, yöntemlere benzer şekilde, lambda ifadelerinin de parametreleri olabilir. Örneğin,

 (n) -> (n%2)==0

Here, the variable n inside the parenthesis is a parameter passed to the lambda expression. The lambda body takes the parameter and checks if it is even or odd.

Example 4: Using lambda expression with parameters

 @FunctionalInterface interface MyInterface ( // abstract method String reverse(String n); ) public class Main ( public static void main( String() args ) ( // declare a reference to MyInterface // assign a lambda expression to the reference MyInterface ref = (str) -> ( String result = ""; for (int i = str.length()-1; i>= 0 ; i--) result += str.charAt(i); return result; ); // call the method of the interface System.out.println("Lambda reversed = " + ref.reverse("Lambda")); ) )

Output:

 Lambda reversed = adbmaL

Generic Functional Interface

Till now we have used the functional interface that accepts only one type of value. For example,

 @FunctionalInterface interface MyInterface ( String reverseString(String n); )

The above functional interface only accepts String and returns String. However, we can make the functional interface generic, so that any data type is accepted. If you are not sure about generics, visit Java Generics.

Example 5: Generic Functional Interface and Lambda Expressions

 // GenericInterface.java @FunctionalInterface interface GenericInterface ( // generic method T func(T t); ) // GenericLambda.java public class Main ( public static void main( String() args ) ( // declare a reference to GenericInterface // the GenericInterface operates on String data // assign a lambda expression to it GenericInterface reverse = (str) -> ( String result = ""; for (int i = str.length()-1; i>= 0 ; i--) result += str.charAt(i); return result; ); System.out.println("Lambda reversed = " + reverse.func("Lambda")); // declare another reference to GenericInterface // the GenericInterface operates on Integer data // assign a lambda expression to it GenericInterface factorial = (n) -> ( int result = 1; for (int i = 1; i <= n; i++) result = i * result; return result; ); System.out.println("factorial of 5 = " + factorial.func(5)); ) )

Output:

 Lambda reversed = adbmaL factorial of 5 = 120

In the above example, we have created a generic functional interface named GenericInterface. It contains a generic method named func().

Here, inside the Main class,

  • GenericInterface reverse - creates a reference to the interface. The interface now operates on String type of data.
  • GenericInterface factorial - creates a reference to the interface. The interface, in this case, operates on the Integer type of data.

Lambda Expression and Stream API

The new java.util.stream package has been added to JDK8 which allows java developers to perform operations like search, filter, map, reduce, or manipulate collections like Lists.

For example, we have a stream of data (in our case a List of String) where each string is a combination of country name and place of the country. Now, we can process this stream of data and retrieve only the places from Nepal.

For this, we can perform bulk operations in the stream by the combination of Stream API and Lambda expression.

Example 6: Demonstration of using lambdas with the Stream API

 import java.util.ArrayList; import java.util.List; public class StreamMain ( // create an object of list using ArrayList static List places = new ArrayList(); // preparing our data public static List getPlaces()( // add places and country to the list places.add("Nepal, Kathmandu"); places.add("Nepal, Pokhara"); places.add("India, Delhi"); places.add("USA, New York"); places.add("Africa, Nigeria"); return places; ) public static void main( String() args ) ( List myPlaces = getPlaces(); System.out.println("Places from Nepal:"); // Filter places from Nepal myPlaces.stream() .filter((p) -> p.startsWith("Nepal")) .map((p) -> p.toUpperCase()) .sorted() .forEach((p) -> System.out.println(p)); ) )

Output:

 Places from Nepal: NEPAL, KATHMANDU NEPAL, POKHARA

In the above example, notice the statement,

 myPlaces.stream() .filter((p) -> p.startsWith("Nepal")) .map((p) -> p.toUpperCase()) .sorted() .forEach((p) -> System.out.println(p));

Here, we are using the methods like filter(), map() and forEach() of the Stream API. These methods can take a lambda expression as input.

Yukarıda öğrendiğimiz söz dizimine göre kendi ifadelerimizi de tanımlayabiliriz. Bu, yukarıdaki örnekte gördüğümüz gibi, kod satırlarını büyük ölçüde azaltmamızı sağlar.

Ilginç makaleler...