Skip to content

4. Java 8'deki lambda fonksiyonları

4.1. Örnek-01 - İşlevsel arayüzler ve lambda fonksiyonları

  

Aşağıdaki kodu ele alalım:


package dvp.java8.lambdas;

public class Exemple01 {
  public static void main(String[] args) {

    // anonim sınıflar
    I1 ia1 = new I1() {
      @Override
      public void doSomething() {
        System.out.println("ia1.doSomething");
      }
    };

    I2 ia2 = new I2() {
      @Override
      public String getSomething(double value) {
        return String.format("ia2.getSomething(%s)", value);
      }
    };

    // lambda ifadeleri
    I1 ib1 = () -> System.out.println("ib1.lambda");
    I2 ib2 = (value) -> String.format("ib2.lambda(%s)", value);
    I1 ib3 = () -> {
      System.out.println("ib3.lambda");
    };
    I2 ib4 = (double value) -> {
      return String.format("ib4.lambda(%s)", value);
    };

    // uygulama
    ia1.doSomething();
    System.out.println(ia2.getSomething(4.3));
    ib1.doSomething();
    System.out.println(ib2.getSomething(5.8));
    ib3.doSomething();
    System.out.println(ib4.getSomething(10.1));
  }
}

@FunctionalInterface
interface I1 {
  void doSomething();
}

@FunctionalInterface
interface I2 {
  String getSomething(double value);
}
  • 41-44. satırlar: I1 işlevsel arayüzünü tanımlar. İşlevsel arayüz, yalnızca bir yönteme sahip olan bir arayüzdür. Bu, 41. satırdaki isteğe bağlı [@FunctionalInterface] anotasyonunun varlığıyla bağlantılı değildir;
  • 46-49. satırlar: ikinci bir işlevsel arayüz olan I2;
  • satır 6-19: I1 ve I2 arayüzleri, lambda fonksiyonlarının ortaya çıkmasından önce en yaygın çözüm olan isimsiz sınıflarla uygulanmıştır;
  • 21-29. satırlar: I1 ve I2 arayüzleri lambda fonksiyonlarıyla uygulanmıştır;
  • 7-12. satırlar: I1 arayüzünün isimsiz bir sınıfla uygulanması. I arayüzünü isimsiz bir sınıfla uygulamak için kullanılan sözdizimi şöyledir:
I i=new I(){
    @Override
    public T1 m1(...){
...
    }
    public T2 m2(...){
...
    }
}

burada m1, m2, ..., I arayüzü tarafından tanımlanan yöntemlerdir.

  • 14-19. satırlar: I2 arayüzünün isimsiz bir sınıfla uygulanması;
  • 22. satır: I1 arayüzünün bir lambda fonksiyonu ile uygulanması. Burada, fonksiyonel arayüzün tek bir yönteme sahip olmasından yararlanılır. Lambda fonksiyonu, bu tek M yöntemini uygular. Sözdizimi şöyledir:
(T1 param1, T2 param2, ...) -> {implémentation de la méthode M ;}

T1, T2 ve Tn türleri, derleyici bunları bağlamdan çıkarabiliyorsa (tür çıkarım) atlanabilir.

  • 22. satır: İmzası aşağıdaki gibi olan [I1.doSomething] yönteminin uygulaması:

void doSomething();

[doSomething], hiçbir parametre almayan ve sonuç döndürmeyen bir yöntemdir. Bu lambda uygulaması, 22. satırdaki gibi ya da 24-26. satırlardaki gibi yazılabilir; yani lambda fonksiyonunun kodunun etrafına küme parantezleri konulabilir. Bu kod, burada olduğu gibi tek bir komut içeriyorsa, bu küme parantezleri atlanabilir;

  • 23. satır: [I1.getSomething] yönteminin uygulaması; imza:

String getSomething(double value);

[getSomething], [double] türünde bir parametre alır ve [String] türünde bir sonuç döndürür. Lambda uygulaması, 23. satırdakiyle veya 27-29. satırdakiyle aynı olabilir. 23. satırdaki uygulamada:

  • [value] parametresinin türü atlanmıştır. Bu durumda, [getSomething]'in imzasında bulunan [double] türü kullanılacaktır;
  • lambda kodu parantez içine alınmamıştır. Bu durumda lambda'nın sonucu, bu koddaki tek ifadenin değeridir; burada: String.format("ib2.lambda(%s)", value);

27-29. satırlardaki uygulamada:

  • [value] parametresinin türü açıkça belirtilir;
  • Lambda sonucunu döndürmek için [return] kullanılır. Bu durumda, küme parantezleri koymak gerekir;
  • 32-37. satırlar: çeşitli isimsiz fonksiyonlar ve lambda'lar çağrılır;

Elde edilen sonuç şöyledir:

1
2
3
4
5
6
ia1.doSomething
ia2.getSomething(4.3)
ib1.lambda
ib2.lambda(5.8)
ib3.lambda
ib4.lambda(10.1)

4.2. Örnek-02 - Predicate<T> işlevsel arayüzü

  

Çoğu zaman, kendimizin tanımladığı fonksiyonel arayüzlerden ziyade, kütüphanelerdeki fonksiyonel arayüzlerle uğraşırız. Burada, Java 8'in işlevsel arayüzlerinin çoğunu bir araya getiren [java.util.function] paketinde tanımlanan [Predicate] işlevsel arayüzüyle ilgileniyoruz. Bu arayüz şu şekilde tanımlanmıştır:

Image

Daha önce bir işlevsel arayüzün yalnızca bir yönteme sahip olduğunu belirtmiştik. Oysa burada dört yöntem bulunmaktadır. Java 8’in getirdiği bir diğer yenilik, [default] anahtar sözcüğüyle karakterize edilen arayüzün varsayılan yöntemi kavramının getirilmesidir. Burada bu tür üç yöntem bulunmaktadır. Bu yöntemlerin özelliği, varsayılan bir uygulamaya sahip olmalarıdır. Dolayısıyla, varsayılan yöntemleri olan bir arayüzü uygulayan bir sınıfın bu yöntemleri uygulaması zorunlu değildir. Böylece, [Predicate] arayüzünü uygulamak isteyen bir sınıfın zorunlu olarak uygulaması gereken tek bir yöntem vardır: [test] yöntemi. Dolayısıyla [Predicate] arayüzü, işlevsel bir arayüzdür. Buna göre, işlevsel bir arayüz, uygulamasında yalnızca bir zorunlu yöntem içeren arayüzdür. Birden fazla yöntemi varsa, diğer yöntemlerin [default] anahtar sözcüğünü içermesi gerekir.

[Predicate<T>.test] yöntemi, T türünde bir parametre alır ve bir boole değeri döndürür. Bu arayüz genellikle koleksiyonları filtrelemek için kullanılır. Kullanımını örneklemek için aşağıdaki verileri kullanacağız:


package dvp.data;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Personne {

  public enum Sexe {HOMME,FEMME};

  // veriler
  private String nom;
  private int age;
  private double poids;
  private Sexe sexe;

  // yapıcılar
  public Personne() {

  }

  public Personne(String nom, Sexe sexe, int age, double poids) {
    this.nom = nom;
    this.sexe=sexe;
    this.age = age;
    this.poids = poids;
  }

  // alıcı ve ayarlayıcılar
...

  // toString
  @Override
  public String toString(){
    try {
      return new ObjectMapper().writeValueAsString(this);
    } catch (JsonProcessingException e) {
      return e.getMessage();
    }
  }
}
  • 32-38. satırlar: [toString] yöntemi, kişinin jSON dizesini döndürür;

[Personnes] sınıfı, 3 kişiden oluşan bir liste tanımlar:


package dvp.data;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Arrays;
import java.util.List;

public class Personnes {
  private static List<Personne> personnes = Arrays.asList(new Personne("jean", Personne.Sexe.HOMME, 20, 70),
    new Personne("marie", Personne.Sexe.FEMME, 10, 30), new Personne("camille", Personne.Sexe.FEMME, 30, 55));

  public static List<Personne> get() {
    return personnes;
  }

  public static String toString(List<Personne> liste) {
    try {
      return new ObjectMapper().writeValueAsString(liste);
    } catch (JsonProcessingException e) {
      return e.getMessage();
    }
  }
}
  • satır 10-11: 3 kişiden oluşan liste;
  • 13-15. satırlar: bu listeyi elde etmek için statik bir yöntem;
  • satır 17-23: parametre olarak geçirilen bir kişi listesinden jSON dizesini elde etmeyi sağlayan statik bir yöntem;

Bu veriler aşağıdaki kod tarafından işlenecektir:


package dvp.java8.lambdas;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

import dvp.data.Personne;
import dvp.data.Personnes;

public class Exemple02 {
  public static void main(String[] args) {
    // anonim sınıf tarafından uygulanan predikat
    Predicate<Personne> filterPoids=new Predicate<Personne>() {
      @Override
      public boolean test(Personne personne) {
        return personne.getPoids()<50;
      }
    };
// bir lambda tarafından uygulanan predikat
    Predicate<Personne> filterAge = p -> p.getAge() < 28;
    // kişi listesi
    List<Personne> personnes = Personnes.get();
    // görüntülemeler
    System.out.println(Personnes.toString(filterPersonnes(personnes, filterAge)));
    System.out.println(Personnes.toString(filterPersonnes(personnes, filterPoids)));
  }

  private static List<Personne> filterPersonnes(List<Personne> personnes, Predicate<Personne> filter) {
      // [filter] listesini filtreler [personnes]
    List<Personne> personnesFiltrées = new ArrayList<>();
    for (Personne p : personnes) {
      if (filter.test(p)) {
        personnesFiltrées.add(p);
      }
    }
    return personnesFiltrées;  }
}
  • satır 13-18: Predicate<Kişi> arayüzünün isimsiz bir sınıfla uygulanması. Bu, kişinin kilosuna göre bir filtredir;
  • 20. satır: Predicate<Kişi> arayüzünün bir lambda fonksiyonu ile uygulanması. Bu, kişinin yaşına göre bir filtredir. Daha önce belirtildiği gibi, bu kod şu şekilde de yazılabilirdi:

        Predicate<Personne> filterAge = (Personne p) -> {
            return (p.getAge() < 28);
        };

ancak 20. satırdaki versiyon daha özlüdür. p parametresinin türü bağlamdan çıkarılır. Burada [Predicate<Personne>] türü oluşturulur. Dolayısıyla, uygulanan yöntemin imzası [boolean test(Personne param)] olur. Bu nedenle, 20. satırdaki p'in örtük türü, [Personne] türüdür;

  • 22. satır: önceden tanımlanmış bir kişi listesi alınır;
  • 24. satır: kişiler yaşlarına göre filtrelenir;
  • 25. satır: Kişiler kilolarına göre filtrelenir. Her iki durumda da, filtrelenmiş listeden jSON dizesi görüntülenir;
  • 28-37. satırlar:
    • parametre olarak filtrelenecek kişi listesini ve filtreyi kabul eder. Filtre, [Predicate<Personne>] arayüzünün bir örneğidir. Dilin yaygın kullanımına uygun olarak, burada ve belgenin diğer yerlerinde, bir arayüz I'nin örneği derken, I'yi uygulayan bir sınıf C'nin örneğini kastediyoruz;
    • sonuç olarak, bu şekilde filtrelenmiş listeyi döndürür;
  • satır 32: [Predicate] arayüzünün [test] yöntemi kullanılır. Yönteme aktarılan filtreye bağlı olarak, [test] yöntemi:

return personne.getPoids()<50;

ya da


return p.getAge() < 28

[Exemple02] sınıfının çalıştırılması şu sonucu verir:

[{"nom":"jean","age":20,"poids":70.0,"sexe":"HOMME"},{"nom":"marie","age":10,"poids":30.0,"sexe":"FEMME"}]
[{"nom":"marie","age":10,"poids":30.0,"sexe":"FEMME"}]

4.3. Örnek-03 - Function<T,R> işlevsel arayüzü

  

Fonksiyonel arayüz Function<T,R> şu şekilde tanımlanmıştır:

Image

Arayüzün tek yöntemi, R apply(T t) imzasına sahiptir. Genellikle bir Collection<T> türünden yeni bir Collection<R> türü oluşturmak için kullanılır. Bu arayüzü örneklemek için aşağıdaki kodu kullanacağız:


package dvp.java8.lambdas;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dvp.data.Personne;
import dvp.data.Personnes;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

public class Exemple03 {
  public static void main(String[] args) throws JsonProcessingException {
    // anonim sınıfla uygulama
    Function<Personne, String> mapToName = new Function<Personne, String>() {
      @Override
      public String apply(Personne personne) {
        return personne.getNom();
      }
    };
    // lambda ile uygulama
    Function<Personne, Integer> mapToAge = p -> p.getAge();
    // kişi listesi
    List<Personne> personnes = Personnes.get();
    // jSON
    ObjectMapper jsonMapper = new ObjectMapper();
    // görüntülemeler
    System.out.println(jsonMapper.writeValueAsString(mapPersonnes(personnes, mapToName)));
    System.out.println(jsonMapper.writeValueAsString(mapPersonnes(personnes, mapToAge)));
  }

  // List<Kişi> --> List<T> dönüşümü
  private static <T> List<T> mapPersonnes(List<Personne> personnes, Function<Personne, T> mapper) {
    List<T> maps = new ArrayList<>();
    for (Personne p : personnes) {
      maps.add(mapper.apply(p));
    }
    return maps;
  }
}
  • 15-20. satırlar: [Function<Personne, String>] arayüzünü, Personne --> String dönüşümünü gerçekleştirecek anonim bir sınıfla uyguluyoruz;
  • 22. satır: [Function<Personne, Integer>] arayüzünü, Personne --> Integer dönüşümünü gerçekleştirecek bir lambda fonksiyonu ile uyguluyoruz;
  • 33-39. satırlar:
    • iki parametre kabul eder: ilki List<Personne> türünde olup dönüştürülecek kişilerin listesidir. İkincisi Function<Kişi, T> türünde olup, listedeki her kişiden T türünde bir nesne oluşturan bir işlevdir;
    • sonuç olarak, her bir elemanı Kişi -> T dönüşümünden elde edilen List<T> türünde bir liste döndürür;
  • 35-37. satırlar: Personne -> T dönüşümü uygulanır. Yöntemin ikinci parametresi [mapToName] nesnesi ise, Personne -> String dönüşümü gerçekleştirilir. [mapToAge] nesnesi ise, Kişi -> Integer dönüşümü gerçekleştirilir;

Elde edilen sonuç şöyledir:

["jean","marie","camille"]
[20,10,30]

4.4. Örnek-04 - Consumer<T> işlevsel arayüzü

  

Consumer<T> işlevsel arayüzü şu şekilde tanımlanmıştır:

Image

Arayüzün tek yöntemi şu imzaya sahiptir: void accept(T t). Bu yöntem, parametresini kullanır (tüketir) ve herhangi bir sonuç döndürmez. Bunu örneklemek için aşağıdaki kodu kullanacağız:


package dvp.java8.lambdas;

import java.util.List;
import java.util.function.Consumer;

import dvp.data.Personne;
import dvp.data.Personnes;

public class Exemple04 {
    public static void main(String[] args) {
        // kişi listesi
        List<Personne> personnes = Personnes.get();
        // anonim uygulama
        Consumer<Personne> consumerAge = new Consumer<Personne>() {
            @Override
            public void accept(Personne personne) {
                System.out.printf(" age de %s = %s%n", personne.getNom(), personne.getAge());
            }
        };
        // lambda uygulaması
        Consumer<Personne> consumerPoids = p -> System.out.printf(" poids de %s = %s%n", p.getNom(), p.getPoids());        
        // görüntülemeler
        for (Personne p : personnes) {
            consumerAge.accept(p);
        }
        System.out.println("--------");
        for (Personne p : personnes) {
            consumerPoids.accept(p);
        }
    }
}
  • 14-19. satırlar: [Consumer<Personne>] arayüzü, [accept] yöntemi ile kişinin adını ve yaşını görüntüleyen isimsiz bir sınıf tarafından uygulanmaktadır;
  • 21. satır: [Consumer<Personne>] arayüzü, [accept] adlı örtük yöntemi ile kişinin adını ve kilosunu görüntüleyen bir lambda fonksiyonu tarafından uygulanır;
  • 23-25. satırlar: Kişi listesi, [consumerAge] uygulaması tarafından kullanılır;
  • 27-29. satırlar: Kişi listesi, [consumerPoids] uygulaması tarafından kullanılır;

Sonuçlar şöyledir:

1
2
3
4
5
6
7
 age de jean = 20
 age de marie = 10
 age de camille = 30
--------
 poids de jean = 70.0
 poids de marie = 30.0
poids de camille = 55.0

4.5. Örnek-05 - BiConsumer<T,U> işlevsel arayüzü

  

BiConsumer<T,U> işlevsel arayüzü şu şekilde tanımlanmıştır:

Image

Arayüzün tek yöntemi şu imzaya sahiptir: void accept(T t, U u). Bu yöntem, T türünü ve U u ek bilgisini kullanır. Kullanımını aşağıdaki kodla göstereceğiz:


package dvp.java8.lambdas;

import dvp.data.Personne;
import dvp.data.Personnes;

import java.util.List;
import java.util.function.BiConsumer;

public class Exemple05 {
  public static void main(String[] args) {
    // kişi listesi
    List<Personne> personnes = Personnes.get();
    // anonim uygulama
    BiConsumer<Personne, Integer> biconsumerAge = new BiConsumer<Personne, Integer>() {
      @Override
      public void accept(Personne personne, Integer integer) {
        personne.setAge(personne.getAge() + integer);
        System.out.printf("age de %s = %s%n", personne.getNom(), personne.getAge());
      }
    };
    // lambda uygulaması
    BiConsumer<Personne, Integer> biconsumerPoids = (p, i) -> {
      p.setPoids(p.getPoids() + i);
      System.out.printf("poids de %s = %s%n", p.getNom(), p.getPoids());
    };
    // görüntülemeler
    for (Personne p : personnes) {
      biconsumerAge.accept(p, 100);
    }
    System.out.println("--------");
    for (Personne p : personnes) {
      biconsumerPoids.accept(p, 200);
    }
  }
}
  • 14-20. satırlar: BiConsumer<T,U> arayüzünün isimsiz bir sınıfla uygulanması. [apply] yöntemi, ikinci parametresini kullanarak ilk parametre olarak geçirilen kişinin yaşını günceller. Ardından sonucu görüntüler;
  • satır 22-25: BiConsumer<T,U> arayüzünün bir lambda fonksiyonu ile uygulanması. Örtük [apply] yöntemi, ikinci parametresini kullanarak ilk parametre olarak geçirilen kişinin kilosunu günceller. Ardından sonucu görüntüler;
  • satır 27-29: Kişi listesi, [biconsumerAge] uygulamasıyla işlenir;
  • satır 31-33: Kişi listesi, [biconsumerPoids] uygulamasıyla işlenir;

Elde edilen sonuçlar şunlardır:

1
2
3
4
5
6
7
age de jean = 120
age de marie = 110
age de camille = 130
--------
poids de jean = 270.0
poids de marie = 230.0
poids de camille = 255.0

4.6. Örnek-06 - BiFunction<T,U,R> işlevsel arayüzü

  

BiFunction<T,U,R> işlevsel arayüzü şu şekilde tanımlanmıştır:

Image

Arayüzün tek yöntemi şu imzaya sahiptir: R apply(T t, U u). Bu yöntem, [BiConsumer.apply] yöntemine benzemektedir; ancak ikincisi bir sonuç döndürmezken, [BiFunction.apply] yöntemi bir sonuç döndürür. Kullanımını aşağıdaki kodla göstereceğiz:


package dvp.java8.lambdas;

import java.util.List;
import java.util.function.BiFunction;

import dvp.data.Personne;
import dvp.data.Personnes;

public class Exemple06 {
    public static void main(String[] args) {
        // kişi listesi
        List<Personne> personnes = Personnes.get();
        // anonim uygulama
        BiFunction<Personne, Integer, Integer> biFunctionAge = new BiFunction<Personne, Integer, Integer>() {
            @Override
            public Integer apply(Personne personne, Integer integer) {
                return personne.getAge() + integer;
            }
        };
        // lambda uygulaması
        BiFunction<Personne, Integer, Double> biFunctionPoids = (p, i) -> {
            return p.getPoids() + i;
        };
        // görüntülemeler
        for (Personne p : personnes) {
            System.out.printf("age de %s = %s%n", p.getNom(), biFunctionAge.apply(p, 100));
        }
        System.out.println("--------");
        for (Personne p : personnes) {
            System.out.printf("poids de %s = %s%n", p.getNom(), biFunctionPoids.apply(p, 200));
        }
    }
}
  • 14-19. satırlar: BiFunction<Personne, Integer, Integer> arayüzü, isimsiz bir sınıfla uygulanmaktadır. Bu sınıfın [apply] yöntemi, ilk parametre olarak geçirilen kişinin yaşına ikinci parametrenin değerini ekleyerek sonucu döndürür;
  • 21-23. satırlar: BiFunction<Kişi, Tamsayı, Çift> arayüzü bir lambda işleviyle uygulanmıştır. [apply] yöntemi, birinci parametre olarak geçirilen kişinin ağırlığına ikinci parametrenin değerini ekleyerek sonucu döndürür;
  • satır 25-27: [biFunctionAge] uygulaması kişilere uygulanır;
  • 29-31. satırlar: [biFunctionPoids] uygulaması kişilere uygulanır;

Elde edilen sonuçlar şunlardır:

age de jean = 120
age de marie = 110
age de camille = 130
--------
poids de jean = 270.0
poids de marie = 230.0
poids de camille = 255.0

Lambda işlevlerinin yanı sıra, Java 8, T türündeki öğelerden oluşan bir akışı modelleyen Stream<T> türünü de getirmiştir. Bu öğeler, lambda işlevleriyle uygulanan ardışık dönüşümlerden geçebilir. Mümkün olduğunda ve birden fazla işlemci varsa, bu dönüşümler bazen paralel olarak gerçekleştirilebilir.

4.7. Örnek-07 - Supplier<T> işlevsel arayüzü

  

Supplier<T> işlevsel arayüzü şu şekilde tanımlanmıştır:

Image

Arayüzün tek yöntemi, T get() imzasına sahiptir ve bu yöntemin görevi, T türünde bir nesne sağlamaktır.

Bu işlevsel arayüzü aşağıdaki kodla göstereceğiz:


package dvp.java8.lambdas;

import java.util.List;
import java.util.Random;
import java.util.function.Supplier;

import dvp.data.Personne;
import dvp.data.Personnes;

public class Exemple07 {
    public static void main(String[] args) {
        // anonim uygulama
        Supplier<Personne> supplier = new Supplier<Personne>() {
            // kişi listesi
            List<Personne> personnes = Personnes.get();

            // arayüz uygulaması
            @Override
            public Personne get() {
                int i = new Random().nextInt(personnes.size());
                return personnes.get(i);
            }
        };
        // değerlendirme
        for (int i = 0; i < 5; i++) {
            affiche(supplier);
        }
    }

    // Kişi görüntüleme
    public static void affiche(Supplier<Personne> supplier) {
        System.out.println(supplier.get());
    }
}
  • 13-28. satırlar: Supplier<Personne> türünün uygulaması;
  • 31-33. satırlar: [affiche] statik yöntemi, Supplier<Personne> türünde bir parametre bekler;
  • 25-27. satırlar: Supplier<Personne> örneğinin çalıştırılması;

Aşağıdaki sonuçlar elde edilir:

1
2
3
4
5
{"nom":"camille","age":30,"poids":55.0,"sexe":"FEMME"}
{"nom":"marie","age":10,"poids":30.0,"sexe":"FEMME"}
{"nom":"jean","age":20,"poids":70.0,"sexe":"HOMME"}
{"nom":"jean","age":20,"poids":70.0,"sexe":"HOMME"}
{"nom":"camille","age":30,"poids":55.0,"sexe":"FEMME"}