Eğitim Portalı/Sürüm Analizi/Java 22 (Mart 2024) — Detaylı Türkçe Analiz
Sürüm Analizi05-java-versiyon-analizi/15-Java-22

Java 22 (Mart 2024) — Detaylı Türkçe Analiz

| Özellik | Değer | |--------------------|--------------------------------------------------------------| | Sürüm | Java SE 22 / JDK 22 | | Çıkış tarihi | 19 Mart 2024 | | Tür | Feature release (non-LTS) — kısa destek…

Java 22 (Mart 2024) — Detaylı Türkçe Analiz

Sürüm Bilgisi

ÖzellikDeğer
SürümJava SE 22 / JDK 22
Çıkış tarihi19 Mart 2024
TürFeature release (non-LTS) — kısa destekli ara sürüm
Önceki LTSJava 21 (Eylül 2023)
Sonraki LTSJava 25 (Eylül 2025)
Toplam JEP sayısı12

Not — non-LTS ne demek? Java 22, üretim ortamı için uzun vadeli destek (LTS) almayan bir ara sürümdür. Genellikle yeni özellikleri denemek, kütüphane uyumluluğunu test etmek ve bir sonraki LTS'e (Java 25) hazırlanmak için kullanılır. Üretimde uzun ömür isteyen ekipler LTS sürümlerde (17, 21, 25 ...) kalmayı tercih eder.

Bu dokümandaki özellikler ve durumları

JEPÖzellikDurum (Java 22)İlgili dosya
454Foreign Function & Memory APIKALICI (stable)ForeignFunctionMemoryApi.java
456Unnamed Variables & PatternsKALICI (stable)UnnamedVariables.java
461Stream GatherersPreview (önizleme)StreamGatherersPreview.java
447Statements before super(...)Preview (önizleme)(sadece bu README'de)
458Launch Multi-File Source-Code ProgramsKALICI (stable)(sadece bu README'de)
423Region Pinning for G1KALICI (stable)(sadece bu README'de)

Genel Bakış

Java 22'nin öne çıkan başlığı, Project Panama'nın olgunlaşmasıyla Foreign Function & Memory (FFM) API'nin artık kalıcı/standart hale gelmesidir. Bu, JNI'nin (Java Native Interface) modern, güvenli ve performanslı alternatifidir ve C/C++ kütüphanelerini çağırmayı köklü biçimde kolaylaştırır.

Bunun yanında Unnamed Variables & Patterns (alt çizgi _) kalıcı hale gelerek kullanılmayan değişkenleri ifade etmeyi standartlaştırdı. Stream Gatherers ise Stream API'ye özel ara operasyon yazma yeteneği getiren (henüz preview) güçlü bir eklemedir.


1. Foreign Function & Memory API — JEP 454 (KALICI)

NEDİR?

java.lang.foreign paketi altında, Java kodundan yerel (native) kod ve kütüphaneleri çağırmayı ve JVM yığını dışındaki (off-heap) belleği güvenli biçimde yönetmeyi sağlayan API'dir. Project Panama'nın ürünüdür.

NEDEN GELDİ?

Onlarca yıldır native kod çağırmanın tek "resmi" yolu JNI idi. JNI:

  • Ayrı C/C++ ara katman (glue) kodu yazmayı zorunlu kılar,
  • Bu kodu derleyip ayrı bir paylaşımlı kütüphane (.so/.dll/.dylib) üretmeyi gerektirir,
  • Kırılgan, hata yapmaya açık (manuel referans yönetimi, çökme riski),
  • Bakımı pahalı ve platforma bağımlıdır.

FFM bu adımların tamamını ortadan kaldırır: ara C kodu YAZMADAN, doğrudan Java'dan native fonksiyon çağrılır.

NE İŞE YARAR?

  • İşletim sisteminin C kütüphanesi (libc) fonksiyonlarını çağırma,
  • Üçüncü parti C/C++ kütüphanelerini (OpenSSL, SQLite, BLAS, CUDA sarmalayıcı vb.) kullanma,
  • Büyük/uzun ömürlü veriyi GC baskısı olmadan off-heap bellekte tutma.

NEREDE KOLAYLIK SAĞLAR?

  • Yüksek performanslı yerel kütüphane entegrasyonları,
  • Büyük veri tamponları (görüntü/ses işleme, ağ tamponları),
  • Mevcut C ekosistemini Java'ya köprülemek.

Temel Bileşenler

BileşenGörevi
LinkerJava ↔ native ABI köprüsü (Linker.nativeLinker())
SymbolLookupKütüphanedeki fonksiyon sembollerinin (adreslerinin) bulunması
FunctionDescriptorNative fonksiyon imzasının (dönüş + parametre) tanımı
MethodHandleÇağrılabilir handle — downcall (Java→native)
upcallStubJava metodunu native callback'e çevirir — upcall
ArenaBellek yaşam döngüsü yönetimi (try-with-resources ile güvenli)
MemorySegmentOff-heap bellek bloğu (pointer'ın güvenli soyutlaması)
ValueLayoutBellek yerleşimi/boyutu (JAVA_INT, JAVA_LONG, ADDRESS...)

ESKİ (JNI) vs YENİ (FFM)

ESKİ — JNI ile strlen çağırmak için gereken adımlar:

/* native.c — AYRICA derlenip .so/.dll yapılması gerekir */
#include <jni.h>
#include <string.h>
JNIEXPORT jlong JNICALL Java_Demo_strlen(JNIEnv *env, jobject obj, jstring s) {
    const char *cstr = (*env)->GetStringUTFChars(env, s, NULL);
    jlong len = (jlong) strlen(cstr);
    (*env)->ReleaseStringUTFChars(env, s, cstr);  // unutulursa memory leak!
    return len;
}
// Java tarafı
public class Demo {
    static { System.loadLibrary("native"); }   // .so/.dll yüklenmeli
    native long strlen(String s);
}
  • javac, javah/-h, gcc -shared, doğru java.library.path derdi...

YENİ — FFM ile (sadece Java, ara C kodu YOK):

Linker linker = Linker.nativeLinker();
MethodHandle strlen = linker.downcallHandle(
        linker.defaultLookup().find("strlen").orElseThrow(),
        FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));

try (Arena arena = Arena.ofConfined()) {
    MemorySegment cString = arena.allocateUtf8String("Merhaba");
    long len = (long) strlen.invoke(cString);   // bellek otomatik serbest kalır
}

FFM'nin JNI'ye Göre Avantajları

  • Güvenli bellek erişimi: MemorySegment sınır kontrolü yapar; rastgele pointer aritmetiği yerine denetimli erişim.
  • Yaşam döngüsü yönetimi: Arena (özellikle ofConfined) try-with-resources ile kapanınca tüm bellek deterministik ve güvenli serbest bırakılır — use-after-free ve leak riski azalır.
  • Ara C kodu yok: Derleme/bağlama zinciri ortadan kalkar.
  • İki yönlü: downcall (Java→native) ve upcall (native→Java callback) destekli.

GERÇEK HAYAT ÖRNEĞİ

Bir görüntü işleme uygulaması, hızlı bir C kütüphanesindeki (ör. libjpeg/libpng) çözme fonksiyonunu çağırıp sonucu büyük bir off-heap MemorySegment'te tutar; böylece GC'yi büyük byte dizileriyle yormaz. ForeignFunctionMemoryApi.java dosyasında bunun çekirdeği gösterilmiştir: libc'den strlen, abs (downcall) ve qsort (Java karşılaştırıcıyı upcall olarak vererek) çağrılır.

➡️ İlgili dosya: ForeignFunctionMemoryApi.java Derleme: javac --release 22 ForeignFunctionMemoryApi.java Çalıştırma: java --enable-native-access=ALL-UNNAMED ForeignFunctionMemoryApi

Geçişte Ne Değişti / Avantaj / Dezavantaj / Risk

  • Değişti: Java 19–21'de preview olan API artık Java 22'de --enable-preview GEREKTİRMEZ. Paket/sınıf adları stabil; ileri uyumlu kullanılabilir.
  • Avantaj: Daha az kod, daha güvenli bellek, daha kolay bakım, daha iyi performans potansiyeli.
  • Dezavantaj/Risk: Native çağrı doğası gereği hâlâ "güvensiz" bir sınırdır; yanlış imza/yerleşim JVM çökmesine yol açabilir. Çalışma zamanında --enable-native-access ile izin gerekir (aksi halde uyarı). Platform/ABI farklılıkları taşınabilirliği etkileyebilir.

2. Unnamed Variables & Patterns — JEP 456 (KALICI)

NEDİR?

Tek alt çizgi _ ile isimsiz değişken ve isimsiz pattern bildirme yeteneği. "Bunu bilerek kullanmıyorum" niyetini sözdiziminde açıkça ifade eder.

NEDEN GELDİ?

Çoğu zaman bir değişkeni bildirmek zorunda kalır ama kullanmayız: catch'te yakalanan exception, lambda'nın ikinci parametresi, sadece sayım için dönen döngü değişkeni, record'un ilgilenmediğimiz bileşeni... Bunlara unused, ignored gibi sahte isimler vermek hem gürültü yaratır hem de derleyici/IDE "kullanılmıyor" uyarısı doğurur.

NE İŞE YARAR / NEREDE KOLAYLIK SAĞLAR?

Niyeti netleştirir, gürültüyü ve yanlış uyarıları azaltır. Kullanım yerleri:

  • Yerel değişken atamaları, for döngüleri,
  • catch (Exception _),
  • try (var _ = ...),
  • Lambda parametreleri (k, _) -> ...,
  • Pattern matching'de unnamed pattern: case Point(int x, _).

Aynı kapsamda birden fazla _ kullanılabilir; çünkü _ bir isim değildir ve ona erişilemez — bu yüzden çakışmaz.

ESKİ vs YENİ

// ESKİ
try { ... }
catch (NumberFormatException e) { /* e kullanılmıyor ama bildirmek zorunda */ }

map.forEach((key, value) -> kullan(key));   // value gereksiz isim, uyarı verir

if (obj instanceof Point(int x, int y)) { kullan(x); }  // y boşuna

// YENİ (Java 22)
try { ... }
catch (NumberFormatException _) { }              // niyet açık

map.forEach((key, _) -> kullan(key));            // value yok, uyarı yok

if (obj instanceof Point(int x, _)) { kullan(x); }  // y atlandı

GERÇEK HAYAT ÖRNEĞİ

Bir konfigürasyon ayrıştırıcıda, sözlüğün sadece anahtarlarıyla ilgilenip değerleri yok saymak, ya da bir koordinat record'unun yalnızca bir bileşenini çekmek. UnnamedVariables.java 7 farklı kullanım senaryosunu gösterir.

➡️ İlgili dosya: UnnamedVariables.java Derleme: javac --release 22 UnnamedVariables.java (preview gerekmez)

Geçişte Ne Değişti / Avantaj / Dezavantaj / Risk

  • Değişti: Java 21'de preview olan özellik Java 22'de kalıcı.
  • Avantaj: Daha temiz, niyeti açık kod; daha az uyarı.
  • Risk: Çok eski kodda _ bir değişken adı olarak kullanılmış olabilir; Java 9'dan beri _'ın yalnız başına identifier olması zaten yasaklanmıştı, bu yüzden pratikte çakışma çok düşüktür.

3. Stream Gatherers — JEP 461 (PREVIEW)

NEDİR?

Stream.gather(Gatherer) ile özel ara (intermediate) stream operasyonları yazma yeteneği. Terminal taraftaki Collector'ın ara-operasyon karşılığı gibidir.

NEDEN GELDİ?

Stream API'nin ara operasyon kümesi sabitti (map, filter, flatMap, limit...). Kayan pencere, ardışık tekrarları eleme, koşullu erken durdurma gibi ihtiyaçlar için bunları zincirlemek zor veya imkânsızdı. Gatherer, durum (state) tutabilen, eleman yayabilen, hatta akışı erken sonlandırabilen genişletilebilir bir mekanizma sunar.

NE İŞE YARAR — Hazır gatherer'lar

  • windowFixed(n) — ardışık n'li bloklar (batch),
  • windowSliding(n) — n boyutlu kayan pencere (moving average vb.),
  • fold(init, fn) — tek sonuca katlama,
  • scan(init, fn) — kümülatif/running ara sonuçlar.

Gatherer'ın 4 bileşeni

initializer (state kur) · integrator (her elemanı işle, downstream'e yay) · combiner (paralel state birleştirme) · finisher (akış sonunda kalanı yay).

ESKİ vs YENİ — kayan pencere

// ESKİ: elle indeksleme, hata yapmaya açık döngü
for (int i = 0; i + 3 <= list.size(); i++) {
    var pencere = list.subList(i, i + 3);
    ...
}

// YENİ (Java 22, preview):
stream.gather(Gatherers.windowSliding(3)).map(...)...

GERÇEK HAYAT ÖRNEĞİ

Finans/IoT verisinde hareketli ortalama, ölçümleri batch'lere bölme, kümülatif toplam, veya log akışında ardışık yinelenen satırları sıkıştırma. StreamGatherersPreview.java hem hazır gatherer'ları hem de iki özel gatherer (ardışık tekrarsız + eşik aşılınca erken durdurma) içerir.

➡️ İlgili dosya: StreamGatherersPreview.java Derleme: javac --release 22 --enable-preview StreamGatherersPreview.java Çalıştırma: java --enable-preview StreamGatherersPreview

Geçişte Ne Değişti / Avantaj / Dezavantaj / Risk

  • Durum: Java 22'de preview (Java 23'te 2. preview, Java 24'te kalıcı).
  • Avantaj: Stream'i genişletilebilir kılar; tekrar kullanılabilir özel operasyonlar.
  • Dezavantaj/Risk: Preview olduğu için API ileride değişebilir; üretimde dikkatli kullanılmalı. --enable-preview ile derlenen sınıflar yalnızca aynı sürümün JVM'inde ve yine --enable-preview ile çalışır.

4. Statements before super(...) — JEP 447 (PREVIEW)

NEDİR? (kısa)

Bir kurucu (constructor) içinde, super(...) veya this(...) çağrısından önce çalışan ifadelere (argümanları doğrulama, hazırlama, dönüştürme) izin verir — yan etki this'e dokunmadığı sürece.

NEDEN / NE İŞE YARAR

Eskiden super(...) kurucunun ilk ifadesi olmak zorundaydı. Argüman doğrulaması gibi mantıkları ya yardımcı static metoda taşımak ya da iç içe ifadelere sıkıştırmak gerekiyordu.

// ESKİ: doğrulama, super argümanının içine gömülür
Foo(int x) { super(dogrula(x)); }   // ayrı static metot gerekir

// YENİ (preview): super'den önce ifade
Foo(int x) {
    if (x < 0) throw new IllegalArgumentException("x negatif olamaz");
    super(x);
}

Durum: Java 22'de preview. Derleme --enable-preview gerektirir.


5. Launch Multi-File Source-Code Programs — JEP 458 (KALICI)

NEDİR? (kısa)

java başlatıcısının (launcher) "kaynak-dosya modu" artık birden fazla .java dosyasına yayılan programları, önceden javac ile derlemeye gerek kalmadan çalıştırabilir.

# Tek komut: birinci dosya ana sınıf; bağımlı .java dosyaları otomatik derlenir
java Main.java

Hızlı prototipleme, öğretim ve küçük araçlar için derle-çalıştır adımını kaldırır. Java 11'deki tek-dosya kaynak modunun çok-dosyaya genişlemişidir.


6. Region Pinning for G1 — JEP 423 (KALICI)

NEDİR? (kısa)

G1 çöp toplayıcısında, JNI kritik bölgeleri (ör. GetPrimitiveArrayCritical) sırasında GC'nin tamamen durmasını engelleyen bölge "pinleme" (sabitleme) iyileştirmesi.

NE İŞE YARAR

Eskiden bir thread native kod için bir diziyi "kritik" olarak kilitlediğinde G1, çakışmamak için GC'yi geciktirebiliyordu (gecikme/latency artışı). Region pinning ile yalnızca ilgili bölgeler sabitlenir; GC çalışmaya devam edebilir.

Avantaj: JNI ağırlıklı uygulamalarda daha düşük GC duraklaması ve daha az verim kaybı. Kod değişikliği gerektirmez — JVM içi iyileştirmedir.


Derleme/Çalıştırma Özeti

DosyaDerlemeÇalıştırma
ForeignFunctionMemoryApi.javajavac --release 22 ForeignFunctionMemoryApi.javajava --enable-native-access=ALL-UNNAMED ForeignFunctionMemoryApi
UnnamedVariables.javajavac --release 22 UnnamedVariables.javajava UnnamedVariables
StreamGatherersPreview.javajavac --release 22 --enable-preview StreamGatherersPreview.javajava --enable-preview StreamGatherersPreview

Önizleme (preview) özellikler için derleme ve çalıştırmada --enable-preview şarttır. Kalıcı özellikler (FFM, Unnamed) için gerekmez.


Özet

Java 22, FFM API'yi kalıcı yaparak native entegrasyonu JNI'siz, güvenli ve modern bir hale getirdi; Unnamed Variables'ı standartlaştırarak kod netliğini artırdı; Stream Gatherers ile Stream API'yi genişletilebilir kıldı (preview). Bir non-LTS sürüm olarak, asıl hedef bu yenilikleri Java 25 LTS'ten önce olgunlaştırmaktı.

Kod Örnekleri(3)

ForeignFunctionMemoryApi

ortam gerekir
ForeignFunctionMemoryApi.java
1// =============================================================================
2//  Java 22 - Foreign Function & Memory (FFM) API  (JEP 454 - KALICI / STABLE)
3// =============================================================================
4//
5//  ÖNEMLİ NOT:
6//  -----------
7//  FFM API, Java 19/20/21 boyunca "preview" (önizleme) olarak ilerledi.
8//  JAVA 22 ile birlikte ARTIK STANDART (non-preview / kalıcı) hale geldi.
9//  Yani bu dosyayı --enable-preview bayrağına GEREK OLMADAN derleyebilirsin.
10//
11//  DERLEME:
12//      javac --release 22 ForeignFunctionMemoryApi.java
13//
14//  ÇALIŞTIRMA (native erişim uyarısını susturmak için --enable-native-access):
15//      java --enable-native-access=ALL-UNNAMED ForeignFunctionMemoryApi
16//
17//  NOT: java.lang.foreign paketi modülünde olduğu için ek bir --add-modules
18//  bayrağına gerek yoktur (java.base içindedir).
19//
20//  Bu örnek, işletim sisteminin STANDART C KÜTÜPHANESİ (libc) içindeki
21//  fonksiyonları Java'dan, JNI YAZMADAN doğrudan çağırır:
22//      - strlen(const char*)  : bir C string'in uzunluğunu döner
23//      - abs(int)             : tam sayının mutlak değeri
24//      - printf(const char*)  : C tarafında ekrana yazar (upcall'a benzemese de
25//                               variadic fonksiyon çağrısına örnektir)
26//
27//  Ayrıca bir UPCALL örneği de var: Java tarafında yazılmış bir karşılaştırma
28//  fonksiyonunu, C standart kütüphanesinin qsort fonksiyonuna geri-çağrı
29//  (callback) olarak veriyoruz.
30// =============================================================================
31
32import java.lang.foreign.Arena;
33import java.lang.foreign.FunctionDescriptor;
34import java.lang.foreign.Linker;
35import java.lang.foreign.MemorySegment;
36import java.lang.foreign.SymbolLookup;
37import java.lang.foreign.ValueLayout;
38import java.lang.invoke.MethodHandle;
39import java.lang.invoke.MethodHandles;
40import java.lang.invoke.MethodType;
41
42public class ForeignFunctionMemoryApi {
43
44    public static void main(String[] args) throws Throwable {
45
46        System.out.println("=== Java 22 Foreign Function & Memory (FFM) API ===\n");
47
48        // ---------------------------------------------------------------------
49        //  1) TEMEL KAVRAMLAR
50        // ---------------------------------------------------------------------
51        //  Linker        : Java ile native (yerel) ABI arasındaki köprü.
52        //                  Linker.nativeLinker() platformun C ABI'sini verir.
53        //  SymbolLookup  : Bir kütüphanedeki fonksiyon sembollerini (adreslerini)
54        //                  bulmamızı sağlar.
55        //  FunctionDescriptor : Native fonksiyonun imzasını (dönüş + parametre
56        //                  tiplerini) tanımlar.
57        //  MethodHandle  : Çağrılabilir Java referansı; native fonksiyonu
58        //                  buradan invoke ederiz (downcall).
59        //  Arena         : Bellek (MemorySegment) yaşam döngüsünü yönetir.
60        //                  try-with-resources ile kapanınca tüm bellek
61        //                  GÜVENLİ biçimde serbest bırakılır.
62        //  MemorySegment : Off-heap (yığın dışı / native) bellek bloğu.
63        //  ValueLayout   : Bir değerin bellekteki yerleşimini/boyutunu tanımlar
64        //                  (ör. JAVA_INT, JAVA_BYTE, ADDRESS).
65        // ---------------------------------------------------------------------
66
67        Linker linker = Linker.nativeLinker();
68
69        // Standart C kütüphanesinin sembollerini bul (libc / msvcrt vb.).
70        // Linker'ın varsayılan lookup'ı, platformun standart kütüphanesini içerir.
71        SymbolLookup stdLib = linker.defaultLookup();
72
73        // =====================================================================
74        //  ÖRNEK 1: C  strlen()  fonksiyonunu çağırma (DOWNCALL)
75        // =====================================================================
76        // C imzası:  size_t strlen(const char *s);
77        //  - Dönüş tipi: size_t  -> 64-bit platformda JAVA_LONG
78        //  - Parametre : const char* -> ADDRESS (bir bellek adresi / pointer)
79        ornekStrlen(linker, stdLib);
80
81        // =====================================================================
82        //  ÖRNEK 2: C  abs()  fonksiyonu (DOWNCALL, sadece skaler tipler)
83        // =====================================================================
84        // C imzası:  int abs(int n);
85        ornekAbs(linker, stdLib);
86
87        // =====================================================================
88        //  ÖRNEK 3: C  qsort()  ile UPCALL (Java callback'i C'ye verme)
89        // =====================================================================
90        // C imzası:  void qsort(void *base, size_t nmemb, size_t size,
91        //                       int (*compar)(const void *, const void *));
92        ornekQsortUpcall(linker, stdLib);
93
94        System.out.println("\n=== Tüm FFM örnekleri tamamlandı ===");
95    }
96
97    // -------------------------------------------------------------------------
98    //  ÖRNEK 1: strlen
99    // -------------------------------------------------------------------------
100    private static void ornekStrlen(Linker linker, SymbolLookup stdLib) throws Throwable {
101        System.out.println("--- ÖRNEK 1: C strlen() çağrısı ---");
102
103        // strlen sembolünü bul. Bulunamazsa Optional boş döner.
104        MemorySegment strlenAddr = stdLib.find("strlen")
105                .orElseThrow(() -> new RuntimeException("strlen sembolü bulunamadı"));
106
107        // Fonksiyon imzasını tanımla:
108        //   dönüş: JAVA_LONG (size_t), parametre: ADDRESS (const char*)
109        FunctionDescriptor strlenDesc =
110                FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS);
111
112        // Çağrılabilir MethodHandle (downcall handle) üret.
113        MethodHandle strlen = linker.downcallHandle(strlenAddr, strlenDesc);
114
115        // Arena: ayırdığımız native belleğin yaşam döngüsünü yönetir.
116        // try-with-resources bloğu bitince bellek otomatik ve GÜVENLİ serbest kalır.
117        // (Manuel free() çağrısı YOK -> memory leak ve use-after-free riski düşer.)
118        try (Arena arena = Arena.ofConfined()) {
119            String metin = "Merhaba FFM API!";
120
121            // Java String'i, C'nin beklediği NUL ('\0') ile sonlanan
122            // bir byte dizisine çevirip native belleğe yaz.
123            // NOT: Java 22'de bu metot allocateFrom(String) adındadır.
124            // (Java 21 preview'da adı allocateUtf8String idi -> yeniden adlandırıldı.)
125            MemorySegment cString = arena.allocateFrom(metin);
126
127            // Native fonksiyonu çağır. Dönüş long (size_t).
128            long uzunluk = (long) strlen.invoke(cString);
129
130            System.out.println("  Metin       : \"" + metin + "\"");
131            System.out.println("  Java length : " + metin.length());
132            System.out.println("  C strlen()  : " + uzunluk + " byte (UTF-8 byte sayısı)");
133            // Not: Türkçe karakter içermediği için bu metinde length == strlen.
134            // 'ç','ş' gibi çok-baytlı UTF-8 karakterlerde C strlen byte sayar,
135            // Java length ise UTF-16 kod birimi sayar -> farklı olabilir.
136        }
137        System.out.println();
138    }
139
140    // -------------------------------------------------------------------------
141    //  ÖRNEK 2: abs
142    // -------------------------------------------------------------------------
143    private static void ornekAbs(Linker linker, SymbolLookup stdLib) throws Throwable {
144        System.out.println("--- ÖRNEK 2: C abs() çağrısı ---");
145
146        MemorySegment absAddr = stdLib.find("abs")
147                .orElseThrow(() -> new RuntimeException("abs sembolü bulunamadı"));
148
149        // int abs(int) -> dönüş JAVA_INT, parametre JAVA_INT
150        FunctionDescriptor absDesc =
151                FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT);
152
153        MethodHandle abs = linker.downcallHandle(absAddr, absDesc);
154
155        // Skaler tip olduğu için Arena'ya gerek yok (bellek ayırmıyoruz).
156        int sonuc = (int) abs.invoke(-12345);
157        System.out.println("  abs(-12345) = " + sonuc);
158        System.out.println();
159    }
160
161    // -------------------------------------------------------------------------
162    //  ÖRNEK 3: qsort + UPCALL (Java karşılaştırıcısını C'ye callback olarak ver)
163    // -------------------------------------------------------------------------
164    private static void ornekQsortUpcall(Linker linker, SymbolLookup stdLib) throws Throwable {
165        System.out.println("--- ÖRNEK 3: C qsort() + Java upcall (callback) ---");
166
167        MemorySegment qsortAddr = stdLib.find("qsort")
168                .orElseThrow(() -> new RuntimeException("qsort sembolü bulunamadı"));
169
170        // void qsort(void* base, size_t nmemb, size_t size, comparator)
171        FunctionDescriptor qsortDesc = FunctionDescriptor.ofVoid(
172                ValueLayout.ADDRESS,   // base   (dizinin adresi)
173                ValueLayout.JAVA_LONG, // nmemb  (eleman sayısı)
174                ValueLayout.JAVA_LONG, // size   (her elemanın byte boyutu)
175                ValueLayout.ADDRESS);  // compar (karşılaştırıcı fonksiyon pointer'ı)
176
177        MethodHandle qsort = linker.downcallHandle(qsortAddr, qsortDesc);
178
179        try (Arena arena = Arena.ofConfined()) {
180            int[] veriler = { 42, 7, 1000, -3, 88, 0, 17 };
181            System.out.println("  Sıralama öncesi : " + java.util.Arrays.toString(veriler));
182
183            // Diziyi native belleğe int (4 byte) dizisi olarak ayır ve doldur.
184            // NOT: Java 22'de N elemanlık ayırma için allocate(layout, count) kullanılır.
185            // (Java 21 preview'da allocateArray(layout, count) idi -> yeniden adlandırıldı.)
186            MemorySegment nativeDizi =
187                    arena.allocate(ValueLayout.JAVA_INT, veriler.length);
188            for (int i = 0; i < veriler.length; i++) {
189                nativeDizi.setAtIndex(ValueLayout.JAVA_INT, i, veriler[i]);
190            }
191
192            // ---- UPCALL: Java metodunu C'nin çağırabileceği bir adrese dönüştür ----
193            // C comparator imzası:  int compar(const void* a, const void* b);
194            // Her iki parametre de elemanın adresidir (ADDRESS).
195            FunctionDescriptor comparatorDesc = FunctionDescriptor.of(
196                    ValueLayout.JAVA_INT,  // dönüş: int (<0, 0, >0)
197                    ValueLayout.ADDRESS,   // a
198                    ValueLayout.ADDRESS);  // b
199
200            // Java tarafındaki statik karşılaştırma metodumuza MethodHandle al.
201            MethodHandle comparatorHandle = MethodHandles.lookup().findStatic(
202                    ForeignFunctionMemoryApi.class,
203                    "intKarsilastir",
204                    MethodType.methodType(int.class, MemorySegment.class, MemorySegment.class));
205
206            // Java handle'ını native bir fonksiyon pointer'ına (upcall stub) çevir.
207            MemorySegment comparatorPtr = linker.upcallStub(
208                    comparatorHandle, comparatorDesc, arena);
209
210            // qsort'u çağır. C, sıralama sırasında bizim Java metodumuzu çağıracak.
211            qsort.invoke(
212                    nativeDizi,
213                    (long) veriler.length,
214                    ValueLayout.JAVA_INT.byteSize(), // her int 4 byte
215                    comparatorPtr);
216
217            // Sonucu native bellekten geri oku.
218            int[] sirali = new int[veriler.length];
219            for (int i = 0; i < veriler.length; i++) {
220                sirali[i] = nativeDizi.getAtIndex(ValueLayout.JAVA_INT, i);
221            }
222            System.out.println("  Sıralama sonrası: " + java.util.Arrays.toString(sirali));
223            System.out.println("  (Sıralamayı C qsort yaptı, karşılaştırmayı Java yaptı!)");
224        }
225        System.out.println();
226    }
227
228    /**
229     * C qsort tarafından çağrılan UPCALL hedefi.
230     * Parametreler, sıralanan int elemanlarının native ADRESLERİdir.
231     * Adresten gerçek int değerini okumak için segmenti yeniden yorumlarız.
232     *
233     * @param aPtr birinci elemanın adresi
234     * @param bPtr ikinci elemanın adresi
235     * @return a<b -> negatif, a==b -> 0, a>b -> pozitif
236     */
237    private static int intKarsilastir(MemorySegment aPtr, MemorySegment bPtr) {
238        // Gelen adresler boyutsuzdur; 4 byte'lık int olarak yeniden yorumla.
239        int a = aPtr.reinterpret(ValueLayout.JAVA_INT.byteSize())
240                    .get(ValueLayout.JAVA_INT, 0);
241        int b = bPtr.reinterpret(ValueLayout.JAVA_INT.byteSize())
242                    .get(ValueLayout.JAVA_INT, 0);
243        return Integer.compare(a, b);
244    }
245}
canlı çalıştırma için ortam gerekir

Bu sürüm-analizi örneği tek dosya olarak yerel JDK 21 ile çalıştırılamadı (bağımsız bir main içermeyebilir ya da daha yeni bir JDK / önizleme özelliği gerektirir). İlgili dil özelliği yukarıdaki anlatım ve kodda gösterilmiştir.

StreamGatherersPreview

ortam gerekir
StreamGatherersPreview.java
1// =============================================================================
2//  Java 22 - Stream Gatherers  (JEP 461 - PREVIEW / ÖNİZLEME)
3// =============================================================================
4//
5//  ÖNEMLİ NOT:
6//  -----------
7//  Stream Gatherers, Java 22'de PREVIEW (önizleme) özelliğidir.
8//  Bu nedenle derlerken VE çalıştırırken --enable-preview GEREKLİDİR.
9//  (Java 23'te ikinci preview, Java 24'te kalıcı hale gelmiştir; ama bu
10//   dosya Java 22 hedefiyle hazırlanmıştır.)
11//
12//  DERLEME:
13//      javac --release 22 --enable-preview StreamGatherersPreview.java
14//  ÇALIŞTIRMA:
15//      java --enable-preview StreamGatherersPreview
16//
17//  (NOT: --enable-preview olmadan derleme/çalıştırma HATA verir.)
18//
19//  STREAM GATHERERS NEDİR?
20//  -----------------------
21//  Stream API'de  filter / map / flatMap gibi sabit bir ara (intermediate)
22//  operasyon kümesi vardı; KENDİ ara operasyonunu yazmak mümkün değildi.
23//  Gatherer arayüzü bunu çözer:  Stream.gather(Gatherer) ile ÖZEL ara
24//  operasyonlar tanımlayabiliriz. Terminal taraftaki Collector'ın
25//  "ara operasyon" karşılığı gibidir.
26//
27//  HAZIR (built-in) GATHERER'LAR (java.util.stream.Gatherers):
28//    - windowFixed(n)   : ardışık n'li bloklara böler (kayan değil)
29//    - windowSliding(n) : n boyutlu KAYAN pencereler üretir
30//    - fold(init, fn)   : tek bir sonuca katlar (akış sonunda 1 eleman yayar)
31//    - scan(init, fn)   : kümülatif/running ara sonuçları yayar
32//
33//  Gatherer'ın 4 bileşeni vardır:
34//    initializer() : (opsiyonel) durum (state) nesnesi oluşturur
35//    integrator()  : her elemanı işler, downstream'e eleman yayabilir
36//    combiner()    : (opsiyonel) paralel akışta state'leri birleştirir
37//    finisher()    : (opsiyonel) akış bitince kalan state'i yayar
38// =============================================================================
39
40import java.util.List;
41import java.util.stream.Gatherer;
42import java.util.stream.Gatherers;
43import java.util.stream.Stream;
44
45public class StreamGatherersPreview {
46
47    public static void main(String[] args) {
48        System.out.println("=== Java 22 Stream Gatherers (PREVIEW) ===\n");
49
50        ornek1_windowFixed();
51        ornek2_windowSliding();
52        ornek3_fold();
53        ornek4_scan();
54        ornek5_ozelGatherer_distinctArdisik();
55        ornek6_ozelGatherer_limitWhile();
56
57        System.out.println("\n=== Tüm gatherer örnekleri tamamlandı ===");
58    }
59
60    // -------------------------------------------------------------------------
61    //  1) windowFixed(n) : ardışık SABİT bloklar
62    // -------------------------------------------------------------------------
63    private static void ornek1_windowFixed() {
64        System.out.println("--- 1) Gatherers.windowFixed(3) ---");
65
66        // Gerçek hayat: 8 ölçüm değerini 3'erli partilere böl (batch işleme).
67        List<List<Integer>> bloklar = Stream.of(1, 2, 3, 4, 5, 6, 7, 8)
68                .gather(Gatherers.windowFixed(3))
69                .toList();
70
71        System.out.println("  3'erli bloklar: " + bloklar);
72        // -> [[1,2,3],[4,5,6],[7,8]]  (son blok eksik olabilir)
73    }
74
75    // -------------------------------------------------------------------------
76    //  2) windowSliding(n) : KAYAN pencere
77    // -------------------------------------------------------------------------
78    private static void ornek2_windowSliding() {
79        System.out.println("--- 2) Gatherers.windowSliding(3) ---");
80
81        // Gerçek hayat: hareketli ortalama (moving average) için 3'lü kayan
82        // pencereler oluştur.
83        List<Double> hareketliOrtalama = Stream.of(10, 20, 30, 40, 50)
84                .gather(Gatherers.windowSliding(3))
85                .map(pencere -> pencere.stream()
86                        .mapToInt(Integer::intValue)
87                        .average()
88                        .orElse(0.0))
89                .toList();
90
91        System.out.println("  3'lü kayan pencere ortalamaları: " + hareketliOrtalama);
92        // pencereler: [10,20,30],[20,30,40],[30,40,50] -> [20.0, 30.0, 40.0]
93    }
94
95    // -------------------------------------------------------------------------
96    //  3) fold(init, fn) : tek bir sonuca katla (akış sonunda 1 eleman)
97    // -------------------------------------------------------------------------
98    private static void ornek3_fold() {
99        System.out.println("--- 3) Gatherers.fold ---");
100
101        // Gerçek hayat: kelimeleri tek bir cümleye katla.
102        String cumle = Stream.of("Java", "22", "Stream", "Gatherers")
103                .gather(Gatherers.fold(
104                        () -> "",                                  // başlangıç değeri
105                        (acc, kelime) -> acc.isEmpty()
106                                ? kelime
107                                : acc + " " + kelime))             // birleştirme
108                .findFirst()
109                .orElse("");
110
111        System.out.println("  fold sonucu: \"" + cumle + "\"");
112    }
113
114    // -------------------------------------------------------------------------
115    //  4) scan(init, fn) : kümülatif (running) sonuçları yay
116    // -------------------------------------------------------------------------
117    private static void ornek4_scan() {
118        System.out.println("--- 4) Gatherers.scan (kümülatif toplam) ---");
119
120        // Gerçek hayat: aylık gelirlerin kümülatif (yıl-içi birikimli) toplamı.
121        List<Integer> kumulatif = Stream.of(100, 200, 300, 400)
122                .gather(Gatherers.scan(() -> 0, (toplam, deger) -> toplam + deger))
123                .toList();
124
125        System.out.println("  Kümülatif toplamlar: " + kumulatif);
126        // -> [100, 300, 600, 1000]
127    }
128
129    // -------------------------------------------------------------------------
130    //  5) ÖZEL GATHERER: ardışık tekrarları temizle (distinctArdisik)
131    //     [1,1,2,2,2,3,1] -> [1,2,3,1]
132    //     (Stream.distinct globaldir; bu sadece ARDIŞIK tekrarı atar.)
133    // -------------------------------------------------------------------------
134    private static void ornek5_ozelGatherer_distinctArdisik() {
135        System.out.println("--- 5) ÖZEL Gatherer: ardışık tekrarları at ---");
136
137        List<Integer> sonuc = Stream.of(1, 1, 2, 2, 2, 3, 1, 1)
138                .gather(ardisikTekrarsiz())
139                .toList();
140
141        System.out.println("  Ardışık tekrarsız: " + sonuc);
142        // -> [1, 2, 3, 1]
143    }
144
145    /**
146     * State'li (durum tutan) özel bir Gatherer.
147     * Bir önceki elemanı hatırlar; yeni eleman ondan farklıysa downstream'e yayar.
148     *
149     *  T tipi:  giriş ve çıkış elemanları (aynı tip)
150     *  state :  son görülen değeri tutan tek elemanlı dizi (mutable kapsül)
151     */
152    private static <T> Gatherer<T, ?, T> ardisikTekrarsiz() {
153        return Gatherer.ofSequential(
154                // initializer: state = "henüz eleman görülmedi" işaretçili kapsül
155                () -> new Object[]{ false, null },   // [gorulduMu, sonDeger]
156                // integrator: her eleman için çalışır
157                Gatherer.Integrator.ofGreedy((state, eleman, downstream) -> {
158                    boolean gorulduMu = (boolean) state[0];
159                    @SuppressWarnings("unchecked")
160                    T sonDeger = (T) state[1];
161
162                    if (!gorulduMu || !java.util.Objects.equals(sonDeger, eleman)) {
163                        state[0] = true;
164                        state[1] = eleman;
165                        // downstream.push: elemanı bir sonraki aşamaya yay.
166                        // false dönerse akış erken sonlanmış demektir.
167                        return downstream.push(eleman);
168                    }
169                    // Ardışık tekrar -> yayma, akışa devam et.
170                    return true;
171                })
172        );
173    }
174
175    // -------------------------------------------------------------------------
176    //  6) ÖZEL GATHERER: koşul sağlandığı sürece al (limitWhile / takeWhile benzeri)
177    //     Burada downstream.push'in false dönmesiyle ERKEN sonlandırmayı gösteriyoruz.
178    // -------------------------------------------------------------------------
179    private static void ornek6_ozelGatherer_limitWhile() {
180        System.out.println("--- 6) ÖZEL Gatherer: toplam belli eşiği aşana kadar al ---");
181
182        // Gerçek hayat: kümülatif toplam 100'ü aşana KADAR elemanları al,
183        // aşan elemandan SONRA akışı erken durdur.
184        List<Integer> sonuc = Stream.of(30, 40, 50, 60, 70)
185                .gather(toplamEsigeKadar(100))
186                .toList();
187
188        System.out.println("  Kümülatif toplam 100'ü aşana kadar: " + sonuc);
189        // 30 (top=30), 40 (top=70), 50 (top=120 -> bunu da al ama sonra dur)
190        // -> [30, 40, 50]
191    }
192
193    private static Gatherer<Integer, ?, Integer> toplamEsigeKadar(int esik) {
194        return Gatherer.ofSequential(
195                () -> new int[]{ 0 },   // state: kümülatif toplam
196                Gatherer.Integrator.of((state, eleman, downstream) -> {
197                    state[0] += eleman;
198                    boolean devam = downstream.push(eleman);
199                    if (state[0] > esik) {
200                        // Eşik aşıldı: bu elemanı yaydık ama akışı SONLANDIR.
201                        return false;   // false -> üst akışı erken bitir
202                    }
203                    return devam;
204                })
205        );
206    }
207}
canlı çalıştırma için ortam gerekir

Bu sürüm-analizi örneği tek dosya olarak yerel JDK 21 ile çalıştırılamadı (bağımsız bir main içermeyebilir ya da daha yeni bir JDK / önizleme özelliği gerektirir). İlgili dil özelliği yukarıdaki anlatım ve kodda gösterilmiştir.

UnnamedVariables

ortam gerekir
UnnamedVariables.java
1// =============================================================================
2//  Java 22 - Unnamed Variables & Patterns  (JEP 456 - KALICI / STABLE)
3// =============================================================================
4//
5//  ÖNEMLİ NOT:
6//  -----------
7//  Bu özellik Java 21'de preview, JAVA 22'de KALICI (final/standart) hale geldi.
8//  Yani --enable-preview bayrağına GEREK YOKTUR.
9//
10//  DERLEME:
11//      javac --release 22 UnnamedVariables.java
12//  ÇALIŞTIRMA:
13//      java UnnamedVariables
14//
15//  ÖZET:
16//  Tek alt çizgi  _  artık "isimsiz değişken / isimsiz pattern" anlamına gelir.
17//  Kullanmayacağımız ama söz dizimi gereği bildirmek zorunda olduğumuz
18//  değişkenleri  _  ile işaretleriz. Bu hem kodu netleştirir ("bunu bilerek
19//  kullanmıyorum" niyetini gösterir) hem de derleyici/IDE uyarılarını azaltır.
20//
21//  KULLANIM YERLERİ:
22//   1) Atama / yerel değişken     :  var _ = pahaliHesap();
23//   2) for döngüsü sayacı / değişkeni
24//   3) catch bloğu parametresi     :  catch (Exception _) { ... }
25//   4) try-with-resources          :  try (var _ = ac()) { ... }
26//   5) Lambda parametresi          :  (k, _) -> ...
27//   6) Pattern matching - unnamed pattern  :  case Point(int x, _)
28//
29//  NOT: Aynı kapsamda BİRDEN FAZLA  _  kullanılabilir (çakışma vermez),
30//  çünkü  _  bir isim değildir; ona erişmek mümkün değildir.
31// =============================================================================
32
33import java.util.ArrayList;
34import java.util.List;
35import java.util.Map;
36import java.util.Queue;
37import java.util.LinkedList;
38
39public class UnnamedVariables {
40
41    // ---- Pattern matching örnekleri için kayıt (record) tipleri ----
42    sealed interface Sekil permits Nokta, Daire, Dikdortgen {}
43    record Nokta(int x, int y) implements Sekil {}
44    record Daire(Nokta merkez, double yaricap) implements Sekil {}
45    record Dikdortgen(Nokta solUst, Nokta sagAlt) implements Sekil {}
46
47    public static void main(String[] args) {
48        System.out.println("=== Java 22 Unnamed Variables & Patterns ===\n");
49
50        ornek1_yerelDegisken();
51        ornek2_forDongusu();
52        ornek3_catchBlogu();
53        ornek4_tryWithResources();
54        ornek5_lambdaParametresi();
55        ornek6_unnamedPattern();
56        ornek7_iciceUnnamedPattern();
57
58        System.out.println("\n=== Tüm örnekler tamamlandı ===");
59    }
60
61    // -------------------------------------------------------------------------
62    //  1) ISIMSIZ YEREL DEĞİŞKEN
63    //     Bir metodun yan etkisi için çağrıldığı, dönüş değerinin
64    //     gerçekten gerekmediği durumlar.
65    // -------------------------------------------------------------------------
66    private static void ornek1_yerelDegisken() {
67        System.out.println("--- 1) Isimsiz yerel değişken ---");
68
69        // queue.poll() yan etki olarak elemanı çıkarır; değeri umursamıyoruz.
70        Queue<String> kuyruk = new LinkedList<>(List.of("a", "b", "c"));
71
72        // ESKİ: int unused = ...; veya değeri gereksiz yere değişkene atama
73        // YENİ: _ ile "bu değeri bilerek kullanmıyorum" deriz.
74        var _ = kuyruk.poll();   // ilk elemanı at, sonucu umursama
75        System.out.println("  Bir eleman atıldı. Kalan kuyruk: " + kuyruk);
76    }
77
78    // -------------------------------------------------------------------------
79    //  2) FOR DÖNGÜSÜNDE ISIMSIZ DEĞİŞKEN
80    //     Sadece "N kere yap" demek istediğimizde sayacı kullanmıyoruz.
81    // -------------------------------------------------------------------------
82    private static void ornek2_forDongusu() {
83        System.out.println("--- 2) for döngüsünde isimsiz değişken ---");
84
85        List<String> kelimeler = List.of("elma", "armut", "kiraz");
86
87        // enhanced-for: elemanı kullanmadan sadece sayım yapıyoruz.
88        int adet = 0;
89        for (var _ : kelimeler) {
90            adet++;   // eleman değerini kullanmıyoruz, sadece sayıyoruz
91        }
92        System.out.println("  Liste eleman sayısı (eleman kullanılmadan): " + adet);
93    }
94
95    // -------------------------------------------------------------------------
96    //  3) CATCH BLOĞUNDA ISIMSIZ DEĞİŞKEN
97    //     Exception nesnesini hiç kullanmayacaksak _ ile yakalarız.
98    // -------------------------------------------------------------------------
99    private static void ornek3_catchBlogu() {
100        System.out.println("--- 3) catch bloğunda isimsiz değişken ---");
101
102        String girdi = "abc";
103        int deger;
104        try {
105            deger = Integer.parseInt(girdi);
106        } catch (NumberFormatException _) {   // exception nesnesini kullanmıyoruz
107            deger = -1;   // geçersiz girdi -> varsayılan
108        }
109        System.out.println("  \"" + girdi + "\" -> " + deger + " (parse başarısızsa -1)");
110    }
111
112    // -------------------------------------------------------------------------
113    //  4) TRY-WITH-RESOURCES İÇİNDE ISIMSIZ DEĞİŞKEN
114    //     Kaynağı sadece açıp kapatmak (kilit/scope) için kullanıyoruz;
115    //     referansa hiç dokunmuyoruz.
116    // -------------------------------------------------------------------------
117    private static void ornek4_tryWithResources() {
118        System.out.println("--- 4) try-with-resources içinde isimsiz kaynak ---");
119
120        // AutoCloseable bir kaynak: kapanınca mesaj yazsın.
121        try (var _ = acKapatLog("VeritabaniBaglantisi")) {
122            System.out.println("  Kaynak kullanılıyor (ama referansına ihtiyaç yok)...");
123        }
124        // try bloğundan çıkınca close() otomatik çağrılır.
125    }
126
127    private static AutoCloseable acKapatLog(String ad) {
128        System.out.println("  [AÇILDI] " + ad);
129        return () -> System.out.println("  [KAPANDI] " + ad);
130    }
131
132    // -------------------------------------------------------------------------
133    //  5) LAMBDA PARAMETRESİNDE ISIMSIZ DEĞİŞKEN
134    //     Bir parametreyi kullanmayacağımızda _ ile geçeriz.
135    // -------------------------------------------------------------------------
136    private static void ornek5_lambdaParametresi() {
137        System.out.println("--- 5) lambda parametresinde isimsiz değişken ---");
138
139        Map<String, Integer> stok = Map.of("kalem", 10, "defter", 5, "silgi", 0);
140
141        // Map.forEach(BiConsumer): value'yu kullanmadan sadece key'leri yaz.
142        System.out.print("  Ürünler: ");
143        List<String> urunler = new ArrayList<>();
144        stok.forEach((urun, _) -> urunler.add(urun));   // miktarı umursamıyoruz
145        System.out.println(urunler);
146
147        // Birden fazla _ aynı lambda içinde de olabilir (örnek amaçlı):
148        // (a, _) gibi -> sadece ilk parametreyi kullan.
149    }
150
151    // -------------------------------------------------------------------------
152    //  6) UNNAMED PATTERN (pattern matching içinde)
153    //     Record desktructuring sırasında ilgilenmediğimiz bileşeni
154    //     _ ile geçeriz. Tip bile yazmamıza gerek yok.
155    // -------------------------------------------------------------------------
156    private static void ornek6_unnamedPattern() {
157        System.out.println("--- 6) Unnamed pattern (record bileşeni atlama) ---");
158
159        Sekil[] sekiller = {
160                new Nokta(3, 7),
161                new Daire(new Nokta(0, 0), 5.0),
162                new Dikdortgen(new Nokta(0, 10), new Nokta(10, 0))
163        };
164
165        for (Sekil s : sekiller) {
166            String aciklama = switch (s) {
167                // Nokta'nın sadece x'iyle ilgileniyoruz, y'yi _ ile atlıyoruz.
168                case Nokta(int x, _) -> "Nokta, x = " + x + " (y göz ardı edildi)";
169
170                // Daire'nin sadece yarıçapıyla ilgileniyoruz, merkezi atlıyoruz.
171                case Daire(var _, double r) -> "Daire, yarıçap = " + r;
172
173                // Dikdörtgen'in iki köşesini de istemiyoruz -> her ikisi de _.
174                case Dikdortgen(var _, var _) -> "Dikdörtgen (köşeler göz ardı edildi)";
175            };
176            System.out.println("  " + aciklama);
177        }
178    }
179
180    // -------------------------------------------------------------------------
181    //  7) İÇ İÇE (NESTED) UNNAMED PATTERN
182    //     Derin yapıların içinde sadece ihtiyacımız olan kısmı çekeriz.
183    // -------------------------------------------------------------------------
184    private static void ornek7_iciceUnnamedPattern() {
185        System.out.println("--- 7) İç içe unnamed pattern ---");
186
187        Object nesne = new Daire(new Nokta(4, 9), 2.5);
188
189        // Daire'nin merkezinin SADECE y koordinatını istiyoruz.
190        // merkez Nokta'sının içine girip x'i _ ile atlıyoruz.
191        if (nesne instanceof Daire(Nokta(_, int merkezY), double r)) {
192            System.out.println("  Dairenin merkez Y'si = " + merkezY
193                    + ", yarıçap = " + r + " (merkez X ve diğerleri atlandı)");
194        }
195    }
196}
canlı çalıştırma için ortam gerekir

Bu sürüm-analizi örneği tek dosya olarak yerel JDK 21 ile çalıştırılamadı (bağımsız bir main içermeyebilir ya da daha yeni bir JDK / önizleme özelliği gerektirir). İlgili dil özelliği yukarıdaki anlatım ve kodda gösterilmiştir.