← Back to list

Java 21 OCP Chapter 8: Lambda ve Fonksiyonel Interface

🔍 Java’da Lambda İfadeleri Nasıl Çalışır?

Arda Onur · 2025-12-08 08:03 · 0 claps · 4.8 min read
#java #java21 #yazilim #lambda #functionalinterface
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud

Java 21 OCP Chapter 8: Lambda ve Fonksiyonel Interface

🔍 Java’da Lambda İfadeleri Nasıl Çalışır?

Java’daki lambda ifadeleri, yalnızca tek bir abstract metoda sahip interfacelerle (Functional Interface) çalışır. Çünkü lambda ifadesi aslında o tek abstract metodun anonim bir implementasyonudur.

🟦 Lambda İfadesinin Temel Yapısı

Bir lambda ifadesi genel olarak şu formu izler:

parametre → gövde

Burada:

  • a → parametre adıdır
  • -> → parametre ile gövdeyi ayırır
  • a.hop() → çalışacak kod bloğudur (body)

🟦 Örnekler

✔ 1) Tek parametre — tür belirtilmeden

a -> a.hop()

✔ 2) Parametre tipi belirtilmiş hâli

(String a) -> a.hop()

Parametre tipini yazarsanız parantez zorunludur.

✔ 3) Block Body Örneği

a -> { return a.hop(); }

🟦 Functional Interface Nedir?

Functional Interface, içerisinde tam olarak bir adet abstract method bulunan interfacetir. Lambda ifadeleri bu interface türleriyle çalışır.

Java bu interface’leri üç şekilde tanımlamamıza izin verir.

🟩 1) Tek abstract method yazarak (en temel tanım)

interface Flyer {
    void fly();
}

Bu interface functional interface’tir çünkü içinde yalnızca bir abstract method vardır.

🟩 2) @FunctionalInterface Annotation’ı ile

Bu annotation zorunlu değildir ama derleyicinin kontrol yapmasını sağlar. Eğer ikinci bir abstract method eklersen, IDE hata verir.

@FunctionalInterface
interface Runner {
    void run();
}

Avantaj: ✔ Yanlışlıkla ikinci abstract method eklemeyi engeller.

🟩 3) Default ve Static Method Eklemek Serbesttir

Functional interface olmayı bozmayan şeyler:

  • default methodlar
  • static methodlar
  • private methodlar (Java 9+)

Çünkü functional interface’i belirleyen tek şey: ➡ Abstract method sayısının sadece 1 olmasıdır.

Bir functional interface’in içinde ekleyebileceğin bazı metodlar abstract sayılmaz ve bu nedenle functional interface özelliğini bozmaz.

Bunlar şunlardır:

  • toString()
  • equals(Object o)
  • hashCode()

✔ Neden bozmaz?

Çünkü bu üç metod Object sınıfından MİRAS gelir.

🟦 Java’da Method Reference Kullanmak

Lambda ifadelerinin daha da kısaltılmış hâlidir. Bir lambda ifadesi zaten var olan bir metodu doğrudan çağırıyorsa, o lambda yerine method reference kullanılabilir.

Method reference, lambdanın yaptığı işi adıyla gösterir:

ClassName::methodName
objectName::methodName
ClassName::new

Lambda → “nasıl yapılacağını” yazar Method reference → “hangi metodun yapılacağını” işaret eder

🟩 1) Static Method Reference

Bir sınıfın static metodunu referans gösterir.

Function<String, Integer> f = Integer::parseInt;

Lambda eşdeğeri:

s -> Integer.parseInt(s)

🟩 2) Instance Method Reference (belirli bir objeye bağlı)

Consumer<String> c = System.out::println;

Lambda eşdeğeri:

x -> System.out.println(x)

🟩 3) Instance Method Reference (sınıf üzerinden)

Parametre, metodun üzerinde çalışacağı nesnedir.

Function<String, Integer> f = String::length;

Lambda eşdeğeri:

s -> s.length()

🟩 4) Constructor Reference (new referansı)

Supplier<ArrayList> s = ArrayList::new;

Lambda eşdeğeri:

() -> new ArrayList()

Ayrıca parametre alan constructor’lar da referanslanabilir:

Function<Integer, ArrayList> f = ArrayList::new;

🟦 Java’da Built-in Functional Interface’lerle Çalışmak

Java, lambda ve method reference kullanabilmemiz için hazır (built-in) functional interface’ler sağlar. Bu sayede kendi interface’imizi tanımlamadan, doğrudan fonksiyonel kod yazabiliriz.

Bu interfaceler java.util.function paketinde bulunur.

🟩 1) Supplier — Bir şey üretir, parametre almaz

Parametre almaz → değer döndürür

Supplier<Double> random = Math::random;
System.out.println(random.get());

🟩 2) Consumer — Bir şey alır, geri döndürmez

Parametre alır → işlem yapar → void döner

Consumer<String> c = s -> System.out.println(s);
c.accept("Hello");

Method reference:

Consumer<String> c = System.out::println;

🟩 3) Function<T, R> — Bir değer alır, başka bir değer döndürür

T parametre → R dönüş tipi

Function<String, Integer> len = String::length;
System.out.println(len.apply("Java")); // 4

🟩 4) Predicate — Şart kontrol eder (true/false döner)

Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(10)); // true

🟩 5) BiConsumer, BiFunction, BiPredicate

İki parametreyle çalışan versiyonlardır:

BiConsumer<String, Integer> print = (a, b) -> System.out.println(a + " = " + b);
print.accept("Age", 25);

🟩 1) UnaryOperator<T>

Bir tane parametre alır ve aynı tipte bir değer döndürür.

Function<T, T> ile tamamen aynı, sadece isim olarak daha anlamlıdır.

✔ Örnek

UnaryOperator<Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 25

🟩 2) BinaryOperator<T>

İki parametre alır ve aynı tipte bir değer döndürür.

BiFunction<T, T, T> ile aynıdır; üç parametre tipi yerine tek bir T kullanır.

✔ Örnek

BinaryOperator<Integer> sum = (a, b) -> a + b;
System.out.println(sum.apply(3, 4)); // 7

🟦 Built-in Functional Interface → Lambda → Method Reference

Her biri üç şekilde kullanılabilir:

1) Lambda ile:

Function<String, Integer> f = s -> s.length();

2) Method reference ile:

Function<String, Integer> f = String::length;

3) Block lambda ile:

Function<String, Integer> f = s -> {
    return s.length();
};

.

🟦 Functional Interfacelerde Convenience Methodlar

Functional interface’lerin çoğu, ana abstract metodun yanında işi kolaylaştıran ek yardımcı metodlar (convenience methods) içerir. Bu metodlar default veya static olarak tanımlanır ve functional interface’in davranışını zincirleme (compose), tersine çevirme (negate), birleştirme (and, or) gibi işlevler ekler.

Bu convenience metodlar functional interface olmalarını bozmaz, çünkü abstract metod sayısı hâlâ 1’dir.

🟩 1) Predicate — and(), or(), negate()

Predicate boolean döndürdüğü için Java sık kullanılan mantıksal kombinasyonları kolaylaştırmış default metodlara sahiptir:

Predicate<String> isShort = s -> s.length() < 5;
Predicate<String> startsWithA = s -> s.startsWith("A");
Predicate<String> combined = isShort.and(startsWithA);
Predicate<String> negated = isShort.negate();

🟦 5) Consumer — andThen()

Birden fazla işlem zincirlemek için:

Consumer<String> c1 = s -> System.out.println("Hello " + s);
Consumer<String> c2 = s -> System.out.println("Length: " + s.length());
Consumer<String> combined = c1.andThen(c2);
combined.accept("Java");

🟩 2) Function — andThen() ve compose()

Function<T, R> zincirleme (pipeline) mantığı için iki convenience method sunar:

✔ andThen() → önce bu, sonra şu

Function<Integer, Integer> times2 = x -> x * 2;
Function<Integer, Integer> plus3 = x -> x + 3;
Function<Integer, Integer> pipeline = times2.andThen(plus3);
pipeline.apply(5); // (5 * 2) + 3 = 13

✔ compose() → önce şu, sonra bu

Function<Integer, Integer> pipeline2 = times2.compose(plus3);
pipeline2.apply(5); // (5 + 3) * 2 = 16

🟦 Primitive Tipler için Functional Interfaces

🟩 1) BooleanSupplier

Bir değer üretir → boolean döner Parametre almaz.

BooleanSupplier isReady = () -> true;
System.out.println(isReady.getAsBoolean()); // true

2) Long Double ve Int için

🟦Lambdalarda değişkenlerle çalışmak

Java’da lambda ifadeleri kapsam (scope) kurallarına sıkı sıkıya bağlıdır.

🟦 Lambdalarda var Kullanımı

Java, lambda parametrelerinde **var** kullanımına izin verir. Eğer lambda parametresi var ile tanımlanırsa:

Compiler, parametrenin tipini otomatik olarak functional interface’in generic tipinden çıkarır.

Yani var, lambdada türü saklamaz, sadece türün inference (çıkarım) yoluyla belirlenmesine izin verir.

🟩 ✔ Örnek — Type Inference (Tip Çıkarımı)

Function<String, Integer> f = (var s) -> s.length();

Burada:

  • Functional interface → Function<String, Integer>
  • Parametre tipi → String
  • var s → compiler bunu String olarak yorumlar.

Dolayısıyla:

(var s) -> s.length()

ile

(String s) -> s.length()

tamamen aynıdır.

🟦 Lambda Body’de Local Variable Kullanma

Java’da lambda ifadeleri, tanımlandıkları metodun içindeki yerel değişkenlere (local variables) erişebilir. Ancak bu erişimin çok önemli bir kuralı vardır:

Lambda body, yalnızca “effectively final” local variable’lara erişebilir.

🟩 ✔ “Effectively Final” Ne Demek?

Bir değişken:

  • final olarak işaretlenmişse veya
  • Değeri hiç değiştirilmemişse

effectively final sayılır.

🟦 ❌ Geçersiz (Invalid) — Parametreyi tekrar tanımlamak

Lambda parametresinin adını lambda body içinde yeniden tanımlayamazsın.

(a) -> {
    int a = 10;   // ❌ compile error: variable a is already defined
    return a;
}

Sonraki konumuzda “Chapter 9| Collections ve Generic” konusunu ele alacağız.

Bana ulaşmak isterseniz;

***LinkedIn Github Instagram***


메타데이터
post_id
cce56dc0e6da
slug
java-21-ocp-chapter-8-lamba-ve-fonksiyonel-interface-cce56dc0e6da
url
https://medium.com/@ardaonur_yazilim/java-21-ocp-chapter-8-lamba-ve-fonksiyonel-interface-cce56dc0e6da
canonical_url
https://medium.com/@ardaonur_yazilim/java-21-ocp-chapter-8-lamba-ve-fonksiyonel-interface-cce56dc0e6da
author_url
https://medium.com/@ardaonur_yazilim
status
ok
fetched_at
2026-07-09 13:13:48