Eğitim Portalı/Sürüm Analizi/Java 14 (Mart 2020) — Detaylı Sürüm Analizi
Sürüm Analizi05-java-versiyon-analizi/07-Java-14

Java 14 (Mart 2020) — Detaylı Sürüm Analizi

> Bu doküman Java 14 sürümüyle gelen önemli dil ve platform özelliklerini Türkçe olarak, > "nedir / neden geldi / ne işe yarar / nerede kolaylık sağlar / eski vs yeni / gerçek hayat" > başlıklarıyla derinlemesine anla…

Java 14 (Mart 2020) — Detaylı Sürüm Analizi

Bu doküman Java 14 sürümüyle gelen önemli dil ve platform özelliklerini Türkçe olarak, "nedir / neden geldi / ne işe yarar / nerede kolaylık sağlar / eski vs yeni / gerçek hayat" başlıklarıyla derinlemesine anlatır. Her özellik için çalışan .java örnek dosyalarına referans verilmiştir.


Sürüm Bilgisi

BilgiDeğer
SürümJava SE 14 / JDK 14
Çıkış Tarihi17 Mart 2020
TürFeature Release (6 aylık döngü)
LTS mi?Hayır (LTS değil; bir sonraki LTS 17'dir)
Önceki sürümJava 13 (Eylül 2019)
Sonraki sürümJava 15 (Eylül 2020)

Java 14'ün Tarihsel Önemi

Java 14, dil seviyesinde "modern Java"nın temellerinin atıldığı sürümlerden biridir. Üç büyük olay aynı anda gerçekleşti:

  1. Switch Expressions KALICI oldu — 12 ve 13'te preview olan özellik artık standart dilin parçası.
  2. Records ilk kez preview olarak geldi — boilerplate'i yok eden veri taşıyıcı sınıflar.
  3. Pattern Matching for instanceof ilk kez preview olarak geldiinstanceof + cast tek adımda.

Ayrıca Helpful NullPointerExceptions özelliği geldi ve debugging'i kökten değiştirdi.


Preview → Kalıcı Evrimi (ÇOK ÖNEMLİ)

Java, yeni dil özelliklerini önce "preview" (önizleme) olarak sunar; topluluk geri bildirimiyle olgunlaştırıp sonraki sürümlerde kalıcı (standart) hale getirir. Java 14'teki üç dil özelliğinin yaşam döngüsü şöyledir:

ÖzellikPreview BaşlangıcıKALICI Olduğu SürümJava 14'teki Durumu
Switch ExpressionsJava 12 (preview), Java 13 (2. preview)Java 14KALICI--enable-preview artık GEREKMEZ
RecordsJava 14 (1. preview)Java 16Preview — --enable-preview --release 14 gerekir
Pattern Matching for instanceofJava 14 (1. preview)Java 16Preview — --enable-preview --release 14 gerekir
Text Blocks (bağlam)Java 13 (preview)Java 152. preview (14'te hâlâ preview)

Preview özelliği nasıl derlenir/çalıştırılır?

javac --enable-preview --release 14 RecordsPreview.java
java  --enable-preview RecordsPreview

Bu repodaki örnekler Java 16+ (Records ve pattern matching kalıcı olduktan sonra) ile --enable-preview OLMADAN da derlenir. Yorumlarda her dosyada Java 14 preview durumu net belirtilmiştir.


Kapsanan Özellikler ve Örnek Dosyalar

#ÖzellikDurumÖrnek Dosya
1Switch ExpressionsKALICISwitchExpressionsKalici.java
2RecordsPreviewRecordsPreview.java
3Pattern Matching for instanceofPreviewPatternMatchingInstanceof.java
4Helpful NullPointerExceptionsStandart (varsayılan açık)HelpfulNullPointer.java
5Foreign-Memory Access APIIncubator(yalnızca bu README'de açıklanmıştır)

1) Switch Expressions (JEP 361) — Artık KALICI

NEDİR

Switch'in bir ifade (expression) olarak değer döndürebilmesidir. Klasik switch bir "deyim" (statement) idi; akış kontrolü yapardı ama doğrudan bir değer üretmezdi. Switch Expressions ile switch artık bir değer döndürür ve onu bir değişkene atayabilirsiniz.

NEDEN GELDİ (Hangi problemi çözüyor)

Klasik switch üç büyük dert çıkarıyordu:

  1. break unutma hatası (fall-through): Her case sonunda break yazmazsanız bir sonraki case'e "düşer" (fall-through) ve sessizce yanlış davranış oluşur — en sık görülen hatalardan biriydi.
  2. Değer döndürememe: Bir değişkene değer atamak için switch'ten ÖNCE değişkeni tanımlayıp her case içinde elle set etmek gerekiyordu.
  3. Sözdizimi gürültüsü: Çok satırlı, tekrarlı case ...: ...; break; blokları.

NE İŞE YARAR

  • -> (ok) söz dizimiyle fall-through tamamen ortadan kalkar (otomatik break).
  • switch doğrudan bir değer döndürür; sonucu değişkene atayabilirsiniz.
  • Çoklu case tek satırda virgülle: case PAZARTESI, SALI, CARSAMBA -> ....
  • Çok satırlı blok gerektiğinde yield ile değer döndürülür.

NEREDE KOLAYLIK SAĞLAR

Enum'lara göre dallanan iş kuralları, durum makineleri (state machine), HTTP durum kodu eşlemeleri, gün/ay hesaplamaları, fiyat/indirim kuralları — yani "girdiye göre bir değer üret" senaryolarının tamamında.

ESKİ vs YENİ

// ESKİ (Java 11 ve öncesi) — break gerektirir, fall-through riski var
int gunSayisi;
switch (ay) {
    case OCAK:
    case MART:
    case MAYIS:
        gunSayisi = 31;
        break;            // unutursan hata!
    case SUBAT:
        gunSayisi = 28;
        break;
    default:
        gunSayisi = 30;
}

// YENİ (Java 14 KALICI) — değer döndürür, break yok, fall-through yok
int gunSayisi = switch (ay) {
    case OCAK, MART, MAYIS -> 31;
    case SUBAT             -> 28;
    default                -> 30;
};

GERÇEK HAYAT ÖRNEĞİ

Bir e-ticaret sisteminde müşteri segmentine göre indirim oranı belirleme; bir bankacılık uygulamasında işlem tipine göre komisyon hesaplama. Detaylı çalışan örnek: SwitchExpressionsKalici.java

Avantaj / Dezavantaj / Risk

  • Avantaj: Daha az kod, fall-through hatası imkânsız, exhaustiveness (enum'da tüm değerleri kapsama) kontrolü derleyici tarafından zorlanır.
  • Dezavantaj: Çok eski Java'ya alışkın ekiplerde okuma alışkanlığı değişimi gerekir.
  • Risk: Yok denecek kadar az; Java 14'ten itibaren tamamen standart.

2) Records (JEP 359) — Preview

NEDİR

record, yalnızca veri taşımak için tasarlanmış, değişmez (immutable) ve özlü (concise) bir sınıf türüdür. Tek satırda tanımladığınız bir record, sizin için otomatik olarak şunları üretir: private final alanlar, tüm alanları alan canonical constructor, her alan için accessor (getter) metotlar, equals(), hashCode() ve toString().

public record Nokta(int x, int y) { }   // Hepsi bu kadar!

NEDEN GELDİ (Hangi problemi çözüyor)

Java'da basit bir veri sınıfı (DTO/POJO) yazmak ACI VERİCİ derecede tekrarlıydı. Sadece x ve y taşıyan bir sınıf için ~50 satır boilerplate yazmak gerekiyordu: constructor, getter'lar, equals, hashCode, toString. Bu kod hem yazması sıkıcı hem bakımı tehlikeliydi — yeni bir alan eklediğinizde equals/hashCode/toString'i güncellemeyi unutmak çok kolaydı ve bu da sinsi hatalara yol açıyordu.

NE İŞE YARAR

  • Tek satırda tam bir veri sınıfı — boilerplate yok.
  • Otomatik immutability (tüm alanlar final).
  • Otomatik, değer tabanlı equals/hashCode (iki record aynı alan değerlerine sahipse eşittir).
  • Okunabilir otomatik toString (Nokta[x=3, y=4]).
  • Compact constructor ile tek yerde validasyon.

NEREDE KOLAYLIK SAĞLAR

DTO'lar (Data Transfer Object), API request/response modelleri, değer nesneleri (value object), metottan birden fazla değer döndürme (tuple yerine), map anahtarları (otomatik equals/hashCode sayesinde), event/mesaj payload'ları.

ESKİ vs YENİ

// ESKİ — basit bir 2D nokta için ~30+ satır boilerplate
public final class NoktaEski {
    private final int x;
    private final int y;
    public NoktaEski(int x, int y) { this.x = x; this.y = y; }
    public int getX() { return x; }
    public int getY() { return y; }
    @Override public boolean equals(Object o) { /* ... */ }
    @Override public int hashCode() { /* ... */ }
    @Override public String toString() { /* ... */ }
}

// YENİ — aynı işi yapan record
public record Nokta(int x, int y) { }

GERÇEK HAYAT ÖRNEĞİ

Bir REST API'de KullaniciDTO, ApiResponse<T>, koordinat/para birimi gibi value object'ler. Compact constructor ile gelen verinin doğrulanması. Derinlemesine çalışan örnek (eski ~50 satır POJO vs tek satır record karşılaştırmasıyla): RecordsPreview.java

Avantaj / Dezavantaj / Risk

  • Avantaj: Devasa boilerplate azalması, immutability, doğru equals/hashCode garantisi.
  • Dezavantaj: Record alanları zorunlu olarak final'dır; "mutable" varlık (entity) modeli için uygun değildir. Kalıtım desteklemez (record'lar implicitly final'dır, başka sınıftan extend edemez).
  • Risk: Java 14'te preview--enable-preview --release 14 gerekir; API/sözdizimi ileride değişebilirdi. Java 16'da KALICI oldu, artık risksizdir.

3) Pattern Matching for instanceof (JEP 305) — Preview

NEDİR

instanceof kontrolü ile birlikte, tip uyuyorsa nesneyi otomatik olarak yeni bir değişkene bağlayan (bind) bir özellik. Ayrıca elle cast yapma ihtiyacını ortadan kaldırır.

if (obj instanceof String s) {
    // 's' burada zaten String tipinde; cast'e gerek yok
    System.out.println(s.length());
}

NEDEN GELDİ (Hangi problemi çözüyor)

instanceof sonrası neredeyse her zaman aynı tipe cast yapılırdı. Bu üç satırlık kalıp (instanceof kontrolü → değişken tanımı → cast) hem tekrarlıydı hem de cast'i yanlış tipe yapma riski taşıyordu (gerçi derleyici çoğunu yakalar ama yine de gürültü).

NE İŞE YARAR

  • Tip kontrolü ve cast tek adımda birleşir; "binding variable" otomatik oluşur.
  • Kod daha kısa, daha güvenli, daha okunaklı olur.
  • Binding değişkeni, koşulun doğru olduğu kapsamda (flow scoping) kullanılabilir.

NEREDE KOLAYLIK SAĞLAR

equals() override etme, generic/Object parametre işleme, JSON/Object parse, ziyaretçi (visitor) benzeri tip-temelli dallanmalar, polymorphic veri işleme.

ESKİ vs YENİ

// ESKİ — kontrol + tanım + cast (3 satır, gereksiz cast)
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

// YENİ — tek satır, otomatik bağlama
if (obj instanceof String s) {
    System.out.println(s.length());
}

GERÇEK HAYAT ÖRNEĞİ

Farklı tipte nesneleri işleyen bir metot ve klasik equals override'ı. Detaylı çalışan örnek: PatternMatchingInstanceof.java

Avantaj / Dezavantaj / Risk

  • Avantaj: Daha az kod, cast hatalarının elenmesi, flow scoping ile temiz kapsam.
  • Dezavantaj: Çok karmaşık koşullarda binding değişkeninin kapsamını takip etmek zihinsel yük getirebilir.
  • Risk: Java 14'te preview. Java 16'da KALICI oldu.

4) Helpful NullPointerExceptions (JEP 358) — Standart

NEDİR

NullPointerException (NPE) mesajlarının, tam olarak hangi değişkenin/metodun null olduğunu açıkça söylemesi. Eskiden NPE sadece satır numarası verirdi; o satırda birden çok null adayı varsa hangisinin patladığını tahmin etmeniz gerekirdi.

NEDEN GELDİ (Hangi problemi çözüyor)

a.getB().getC().getD() gibi zincirleme çağrılarda eski NPE şöyle derdi:

Exception in thread "main" java.lang.NullPointerException
    at com.ornek.App.main(App.java:42)

Bu satırda a, getB()'nin sonucu, getC()'nin sonucu — hangisi null? Belli değil! Debugging saatler alabiliyordu.

NE İŞE YARAR

Java 14'ten itibaren NPE mesajı tam olarak şunu söyler:

Cannot invoke "Sehir.getPostaKodu()" because the return value of
"Adres.getSehir()" is null

Yani: "Adres.getSehir() null döndürdüğü için Sehir.getPostaKodu() çağrılamadı." — Hangi parçanın null olduğu kristal netliğinde.

NEREDE KOLAYLIK SAĞLAR

İç içe nesne grafikleri (object graph), DTO/entity zincirleri, üretim (production) loglarındaki NPE'lerin teşhisi, eğitim/öğrenme. Debugger açmadan logdan doğrudan kök nedeni görmek.

Aktivasyon Notu

  • Java 14'te flag: -XX:+ShowCodeDetailsInExceptionMessages.
  • Java 15'ten itibaren bu davranış VARSAYILAN olarak AÇIKTIR (flag'e gerek yok).
  • Java 14'te varsayılan deneysel/açıktı; kapatmak için -XX:-ShowCodeDetailsInExceptionMessages.

ESKİ vs YENİ

ESKİ (Java 13):
  java.lang.NullPointerException
      at App.main(App.java:42)

YENİ (Java 14+):
  java.lang.NullPointerException: Cannot invoke "Sehir.getPostaKodu()"
  because the return value of "Adres.getSehir()" is null
      at App.main(App.java:42)

GERÇEK HAYAT ÖRNEĞİ

Kullanıcı → Adres → Şehir → PostaKodu zinciri; bilerek null bırakılan bir halka ve yakalanan NPE mesajının yazdırılması. Detaylı çalışan örnek: HelpfulNullPointer.java

Avantaj / Dezavantaj / Risk

  • Avantaj: Debugging süresinde dramatik kısalma, daha anlaşılır production logları.
  • Dezavantaj: Çok nadir durumlarda mesaj oluşturmak değişken adlarını bytecode'dan okuyabilir (varsayılan kapalıyken gizlilik kaygısı vardı; bu yüzden 14'te opsiyoneldi, 15'te açıldı).
  • Risk: Yok; tamamen geriye uyumlu.

5) Foreign-Memory Access API (JEP 370) — Incubator (yalnızca açıklama)

NEDİR

Java programlarının, JVM yığını (heap) DIŞINDAKİ bellekteki (off-heap / native memory) verilere güvenli ve verimli erişebilmesi için tasarlanmış yeni bir API'dir. MemorySegment, MemoryAddress ve MemoryLayout gibi soyutlamalar sunar.

NEDEN GELDİ (Hangi problemi çözüyor)

Java tarihinde off-heap belleğe erişmenin iki yolu vardı ve ikisi de sorunluydu:

  1. ByteBuffer (java.nio): Boyut sınırlı (max ~2GB / Integer.MAX_VALUE), serbest bırakma (deallocation) zamanlaması belirsiz (GC'ye bağlı).
  2. sun.misc.Unsafe: Resmi olmayan, tehlikeli, desteklenmeyen, kaldırılması planlanan bir iç API. Yanlış kullanım JVM'i çökertebiliyordu.

Foreign-Memory Access API, bu iki kötü seçeneğin yerine güvenli, deterministik ve performanslı bir alternatif getirir.

NE İŞE YARAR / NEREDE KOLAYLIK SAĞLAR

  • Büyük native bellek bloklarıyla çalışma (büyük veri, bellek-eşlemeli dosyalar, native kütüphane tamponları).
  • Deterministik bellek yönetimi (ne zaman serbest bırakılacağı kontrol edilebilir).
  • Yüksek performanslı I/O, native interoperability (sonraki sürümlerde Foreign Function & Memory API — Project Panama'nın bir parçası — olarak olgunlaştı).

Durumu

Java 14'te incubator modülüdür (jdk.incubator.foreign). Incubator, preview'dan daha erken bir olgunluk seviyesidir; deneme amaçlıdır ve --add-modules jdk.incubator.foreign ile etkinleştirilir. Project Panama kapsamında Java 21'de Foreign Function & Memory API olarak kalıcılaştı (JEP 442/454). Bu repodaki örneklerde incubator modülü için çalışan kod verilmemiştir (amaca uygun olarak yalnızca kavramsal açıklama).


Java 13'ten 14'e Geçişte Ne Değişti? (Özet)

KonuJava 13Java 14
Switch ExpressionsPreview (2.)KALICI (artık standart)
RecordsYokPreview (1.)
Pattern Matching for instanceofYokPreview (1.)
Helpful NPEYokVar (-XX:+ShowCodeDetailsInExceptionMessages)
Text BlocksPreview (1.)Preview (2.) (15'te kalıcı)
Foreign-Memory AccessYokIncubator
GCMevcutZGC ve Shenandoah olgunlaştı; CMS GC kaldırıldı (JEP 363)

Geçişte Dikkat Edilecekler

  • CMS Garbage Collector kaldırıldı (JEP 363): -XX:+UseConcMarkSweepGC kullanan eski yapılandırmalar artık çalışmaz; G1 (varsayılan) veya ZGC'ye geçilmelidir.
  • Switch Expressions kullanıyorsanız --enable-preview bayraklarını kaldırabilirsiniz (14'te kalıcı oldu).
  • Records ve pattern matching denemek için --enable-preview --release 14 gerekir.

Bu Repoyu Derleme / Çalıştırma

Bu repodaki örnekler Java 16+ (Records ve pattern matching'in kalıcılaştığı sürüm) ile --enable-preview OLMADAN derlenir. Java 21 önerilir:

cd /Users/miracguntogar/Desktop/test/Java-Versiyon-Analizi/07-Java-14/

# Tek tek derleme + çalıştırma (Java 16+):
javac SwitchExpressionsKalici.java && java SwitchExpressionsKalici
javac RecordsPreview.java          && java RecordsPreview
javac PatternMatchingInstanceof.java && java PatternMatchingInstanceof
javac HelpfulNullPointer.java      && java HelpfulNullPointer

Java 14'te (tarihsel doğruluk için) preview özelliklerini derlemek isterseniz:

javac --enable-preview --release 14 RecordsPreview.java
java  --enable-preview RecordsPreview

Helpful NPE Java 14'te flag gerektirir: java -XX:+ShowCodeDetailsInExceptionMessages ... Java 15+ ile varsayılan açıktır.

Kod Örnekleri(4)

HelpfulNullPointer

çalıştırılabilir
HelpfulNullPointer.java
1/*
2 * =============================================================================
3 *  Java 14 - HELPFUL NULLPOINTEREXCEPTIONS
4 *  JEP 358: Helpful NullPointerExceptions
5 * =============================================================================
6 *
7 *  DURUM:
8 *    - Java 14: Geldi. Etkinlestirme bayragi:
9 *               java -XX:+ShowCodeDetailsInExceptionMessages HelpfulNullPointer
10 *    - Java 15 ve sonrasi: VARSAYILAN olarak ACIK (bayraga gerek YOK).
11 *
12 *  Bu ornek Java 15+ (ornegin Java 21) ile dogrudan calistiginda yardimci
13 *  NPE mesajini gosterir. Java 14'te yukaridaki bayrak gerekir.
14 *
15 *  PROBLEM:
16 *    a.getB().getC().getD() gibi ZINCIRLEME erisimlerde, eski NPE mesaji
17 *    sadece satir numarasi verirdi. O satirda birden cok "null adayi" varsa
18 *    HANGISININ null oldugunu tahmin etmeniz gerekirdi -> saatlerce debug.
19 *
20 *  COZUM:
21 *    Java 14+ NPE mesaji TAM OLARAK hangi parcanin null oldugunu soyler:
22 *      'Cannot invoke "Sehir.getPostaKodu()" because the return value of
23 *       "Adres.getSehir()" is null'
24 * =============================================================================
25 */
26public class HelpfulNullPointer {
27
28    public static void main(String[] args) {
29        System.out.println("=== Java 14: Helpful NullPointerExceptions ===\n");
30        System.out.println("(Java 14'te calistirmak icin: "
31                + "java -XX:+ShowCodeDetailsInExceptionMessages HelpfulNullPointer)");
32        System.out.println("(Java 15+ varsayilan ACIK; ek bayrak gerekmez)\n");
33
34        // -------------------------------------------------------------------
35        // GERCEK HAYAT: Ic ice nesne grafigi
36        //   Kullanici -> Adres -> Sehir -> postaKodu
37        // Bilerek bir halkayi null birakiyoruz.
38        // -------------------------------------------------------------------
39
40        // SENARYO 1: Sehir.getPostaKodu() null degil ama Adres.getSehir() null.
41        // Yani zincir ORTASINDA bir halka kopuk.
42        System.out.println("--- SENARYO 1: Adres var, ama Sehir null ---");
43        Kullanici k1 = new Kullanici(
44                "Ayse",
45                new Adres("Bagdat Cad. No:10", /* sehir = */ null) // <-- sehir NULL!
46        );
47        try {
48            // Zincirleme erisim: k1.adres().sehir().postaKodu()
49            String postaKodu = k1.adres().sehir().postaKodu();
50            System.out.println("Posta kodu: " + postaKodu);
51        } catch (NullPointerException e) {
52            // Java 14+ ile mesaj TAM olarak hangi parcanin null oldugunu soyler.
53            System.out.println("NPE yakalandi -> " + e.getMessage());
54            System.out.println("  (Eski Java'da burada SADECE satir numarasi olurdu,"
55                    + " mesaj BOS olurdu.)");
56        }
57        System.out.println();
58
59        // SENARYO 2: Kullanicinin adresi tamamen null.
60        // Zincirin ILK halkasi kopuk.
61        System.out.println("--- SENARYO 2: Kullanicinin adresi null ---");
62        Kullanici k2 = new Kullanici("Mehmet", /* adres = */ null); // <-- adres NULL!
63        try {
64            String postaKodu = k2.adres().sehir().postaKodu();
65            System.out.println("Posta kodu: " + postaKodu);
66        } catch (NullPointerException e) {
67            System.out.println("NPE yakalandi -> " + e.getMessage());
68        }
69        System.out.println();
70
71        // SENARYO 3: Tum zincir saglam -> hata yok.
72        System.out.println("--- SENARYO 3: Zincir saglam (hata yok) ---");
73        Kullanici k3 = new Kullanici(
74                "Zeynep",
75                new Adres("Ataturk Bulv. No:5", new Sehir("Ankara", "06000"))
76        );
77        String postaKodu = k3.adres().sehir().postaKodu();
78        System.out.println("Posta kodu: " + postaKodu);
79        System.out.println();
80
81        // -------------------------------------------------------------------
82        // BONUS: Dizi/alan erisiminde de yardimci mesaj
83        // -------------------------------------------------------------------
84        System.out.println("--- BONUS: Dizi elemani null ---");
85        Sehir[] sehirler = new Sehir[3]; // tum elemanlar null
86        try {
87            String ad = sehirler[1].ad(); // sehirler[1] null
88            System.out.println(ad);
89        } catch (NullPointerException e) {
90            System.out.println("NPE yakalandi -> " + e.getMessage());
91        }
92
93        System.out.println("\nSONUC: Helpful NPE, ZINCIRLEME erisimlerde hangi"
94                + " halkanin koptugunu");
95        System.out.println("       dogrudan soyleyerek debugging suresini dramatik"
96                + " olarak kisaltir.");
97    }
98
99    // =========================================================================
100    //  Ic ice nesne grafigi modeli (gercek hayatta DTO/entity zinciri gibi)
101    // =========================================================================
102    static final class Kullanici {
103        private final String ad;
104        private final Adres adres;
105        Kullanici(String ad, Adres adres) { this.ad = ad; this.adres = adres; }
106        String ad()    { return ad; }
107        Adres  adres() { return adres; }
108    }
109
110    static final class Adres {
111        private final String sokak;
112        private final Sehir sehir;
113        Adres(String sokak, Sehir sehir) { this.sokak = sokak; this.sehir = sehir; }
114        String sokak() { return sokak; }
115        Sehir  sehir() { return sehir; }
116    }
117
118    static final class Sehir {
119        private final String ad;
120        private final String postaKodu;
121        Sehir(String ad, String postaKodu) { this.ad = ad; this.postaKodu = postaKodu; }
122        String ad()        { return ad; }
123        String postaKodu() { return postaKodu; }
124    }
125}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

PatternMatchingInstanceof

çalıştırılabilir
PatternMatchingInstanceof.java
1/*
2 * =============================================================================
3 *  Java 14 - PATTERN MATCHING FOR instanceof (PREVIEW)
4 *  JEP 305: Pattern Matching for instanceof (Preview)
5 * =============================================================================
6 *
7 *  PREVIEW DURUMU (cok onemli):
8 *    - Java 14: instanceof pattern matching ILK preview olarak geldi.
9 *               Derlemek icin:  javac --enable-preview --release 14 PatternMatchingInstanceof.java
10 *               Calistirmak:    java  --enable-preview PatternMatchingInstanceof
11 *    - Java 15: 2. preview.
12 *    - Java 16: ARTIK KALICI! "--enable-preview" gerekmez.
13 *
14 *  Bu dosya Java 16+ (ornegin Java 21) ile preview bayragi OLMADAN derlenir.
15 *
16 *  ANA FIKIR:
17 *    Klasik kalip:  if (obj instanceof String) { String s = (String) obj; ... }
18 *    Yeni kalip:    if (obj instanceof String s)              { ... }
19 *    -> Tip kontrolu + cast + degisken tanimi TEK ADIMDA birlesir.
20 *    -> "s" otomatik olarak String tipinde baglanir (binding variable).
21 * =============================================================================
22 */
23public class PatternMatchingInstanceof {
24
25    public static void main(String[] args) {
26        System.out.println("=== Java 14: Pattern Matching for instanceof (Preview) ===\n");
27
28        // -------------------------------------------------------------------
29        // 1) ESKI vs YENI - aciklayici metin uretme
30        // -------------------------------------------------------------------
31        Object[] nesneler = {
32                "Merhaba Dunya",
33                42,
34                3.14,
35                java.util.List.of("a", "b", "c"),
36                true
37        };
38
39        System.out.println("--- ESKI yol (gereksiz cast ile) ---");
40        for (Object o : nesneler) {
41            System.out.println("  " + aciklaEski(o));
42        }
43
44        System.out.println("\n--- YENI yol (pattern matching ile) ---");
45        for (Object o : nesneler) {
46            System.out.println("  " + aciklaYeni(o));
47        }
48        System.out.println();
49
50        // -------------------------------------------------------------------
51        // 2) GERCEK HAYAT: equals() override - en sik kullanim alani
52        // -------------------------------------------------------------------
53        System.out.println("--- Gercek Hayat: equals() override ---");
54        Urun u1 = new Urun("Klavye", 750);
55        Urun u2 = new Urun("Klavye", 750);
56        Urun u3 = new Urun("Mouse", 300);
57        System.out.println("u1.equals(u2) : " + u1.equals(u2)); // true
58        System.out.println("u1.equals(u3) : " + u1.equals(u3)); // false
59        System.out.println("u1.equals(\"x\"): " + u1.equals("x")); // false
60        System.out.println();
61
62        // -------------------------------------------------------------------
63        // 3) GERCEK HAYAT: Object iceren bir "JSON benzeri" deger isleme
64        // -------------------------------------------------------------------
65        System.out.println("--- Gercek Hayat: JSON benzeri deger formatlama ---");
66        System.out.println("  " + jsonDeger("isim"));
67        System.out.println("  " + jsonDeger(2026));
68        System.out.println("  " + jsonDeger(true));
69        System.out.println("  " + jsonDeger(null));
70        System.out.println("  " + jsonDeger(99.5));
71    }
72
73    /**
74     * ESKI YOL: instanceof kontrolu + ayri cast + degisken tanimi.
75     * Her tip icin 3 satirlik tekrar; cast gurultusu.
76     */
77    static String aciklaEski(Object o) {
78        if (o instanceof String) {
79            String s = (String) o;                 // gereksiz, tekrarli cast
80            return "Metin uzunlugu: " + s.length();
81        } else if (o instanceof Integer) {
82            Integer i = (Integer) o;
83            return "Tam sayi karesi: " + (i * i);
84        } else if (o instanceof Double) {
85            Double d = (Double) o;
86            return "Ondalik yuvarlanmis: " + Math.round(d);
87        } else if (o instanceof java.util.List) {
88            java.util.List<?> list = (java.util.List<?>) o;
89            return "Liste boyutu: " + list.size();
90        }
91        return "Bilinmeyen tip: " + o;
92    }
93
94    /**
95     * YENI YOL: pattern matching - tip kontrolu ve binding tek adimda.
96     * Binding degiskeni (s, i, d, list) dogrudan kullanilabilir; cast YOK.
97     */
98    static String aciklaYeni(Object o) {
99        if (o instanceof String s) {                       // s -> String
100            return "Metin uzunlugu: " + s.length();
101        } else if (o instanceof Integer i) {               // i -> Integer
102            return "Tam sayi karesi: " + (i * i);
103        } else if (o instanceof Double d) {                // d -> Double
104            return "Ondalik yuvarlanmis: " + Math.round(d);
105        } else if (o instanceof java.util.List<?> list) {  // list -> List
106            return "Liste boyutu: " + list.size();
107        }
108        return "Bilinmeyen tip: " + o;
109    }
110
111    /**
112     * JSON benzeri deger formatlama. Pattern matching ile tip-temelli
113     * dallanmanin ne kadar temiz oldugunu gosterir.
114     */
115    static String jsonDeger(Object o) {
116        if (o == null) {
117            return "null";
118        } else if (o instanceof String s) {
119            return "\"" + s + "\"";                 // metinler tirnak icinde
120        } else if (o instanceof Boolean b) {
121            return b ? "true" : "false";
122        } else if (o instanceof Number n) {
123            return n.toString();                    // sayilar tirnaksiz
124        }
125        return "\"" + o + "\"";
126    }
127
128    // =========================================================================
129    //  GERCEK HAYAT: equals() override - pattern matching'in klasik kullanimi
130    //  Tek "&&" kosulunda hem tip kontrolu hem binding hem karsilastirma.
131    // =========================================================================
132    static final class Urun {
133        private final String ad;
134        private final int fiyat;
135
136        Urun(String ad, int fiyat) {
137            this.ad = ad;
138            this.fiyat = fiyat;
139        }
140
141        @Override
142        public boolean equals(Object o) {
143            // ESKI: if (!(o instanceof Urun)) return false; Urun u = (Urun) o; ...
144            // YENI: tek satirda kontrol + binding + flow scoping.
145            // "&&" sayesinde 'u', sadece instanceof TRUE iken erisilebilir
146            // (flow scoping); bu da kodu hem kisaltir hem guvenli kilar.
147            return o instanceof Urun u
148                    && fiyat == u.fiyat
149                    && java.util.Objects.equals(ad, u.ad);
150        }
151
152        @Override
153        public int hashCode() {
154            return java.util.Objects.hash(ad, fiyat);
155        }
156
157        @Override
158        public String toString() {
159            return "Urun{ad='" + ad + "', fiyat=" + fiyat + "}";
160        }
161    }
162}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

RecordsPreview

çalıştırılabilir
RecordsPreview.java
1/*
2 * =============================================================================
3 *  Java 14 - RECORDS (PREVIEW)
4 *  JEP 359: Records (Preview)
5 * =============================================================================
6 *
7 *  PREVIEW DURUMU (cok onemli):
8 *    - Java 14: Records ILK preview olarak geldi.
9 *               Derlemek icin:  javac --enable-preview --release 14 RecordsPreview.java
10 *               Calistirmak:    java  --enable-preview RecordsPreview
11 *    - Java 15: 2. preview.
12 *    - Java 16: ARTIK KALICI! "--enable-preview" gerekmez.
13 *
14 *  Bu dosya Java 16+ (ornegin Java 21) ile preview bayragi OLMADAN derlenir.
15 *
16 *  ANA FIKIR:
17 *    "record", yalnizca VERI tasimak icin tasarlanmis, degismez (immutable),
18 *    ozlu bir siniftir. Tek satirda tanimladiginiz record sizin yerinize sunlari
19 *    OTOMATIK uretir:
20 *       - private final alanlar
21 *       - tum alanlari alan canonical constructor
22 *       - her alan icin accessor (getter) metot:  x(), y(), ad() ...
23 *       - equals(), hashCode(), toString()
24 * =============================================================================
25 */
26import java.util.List;
27import java.util.Map;
28import java.util.Objects;
29
30public class RecordsPreview {
31
32    public static void main(String[] args) {
33        System.out.println("=== Java 14: Records (Preview) ===\n");
34
35        // -------------------------------------------------------------------
36        // BOLUM 1: ESKI vs YENI -- ayni 2D nokta, ayni davranis
37        // -------------------------------------------------------------------
38        System.out.println("--- BOLUM 1: ~50 satir POJO vs 1 satir record ---");
39
40        // ESKI: asagidaki NoktaEski sinifi ~50 satir boilerplate icerir.
41        NoktaEski eski1 = new NoktaEski(3, 4);
42        NoktaEski eski2 = new NoktaEski(3, 4);
43        System.out.println("ESKI POJO  toString : " + eski1);
44        System.out.println("ESKI POJO  equals   : " + eski1.equals(eski2));
45        System.out.println("ESKI POJO  getX     : " + eski1.getX());
46
47        // YENI: ayni isi yapan record TEK SATIR (asagida Nokta tanimi).
48        Nokta yeni1 = new Nokta(3, 4);
49        Nokta yeni2 = new Nokta(3, 4);
50        System.out.println("YENI record toString: " + yeni1);          // Nokta[x=3, y=4]
51        System.out.println("YENI record equals  : " + yeni1.equals(yeni2)); // true
52        System.out.println("YENI record getter  : " + yeni1.x());      // accessor: x()
53        System.out.println("YENI record hashCode esit mi: "
54                + (yeni1.hashCode() == yeni2.hashCode()));             // true
55        System.out.println();
56
57        // -------------------------------------------------------------------
58        // BOLUM 2: Compact constructor ile VALIDASYON
59        // -------------------------------------------------------------------
60        System.out.println("--- BOLUM 2: Compact constructor ile validasyon ---");
61        Para tutar = new Para(150.0, "TRY");
62        System.out.println("Gecerli para: " + tutar);
63        try {
64            new Para(-5.0, "TRY"); // negatif tutar -> compact constructor exception firlatir
65        } catch (IllegalArgumentException e) {
66            System.out.println("Validasyon yakalandi: " + e.getMessage());
67        }
68        try {
69            new Para(10.0, "EURO"); // gecersiz para birimi kodu (3 harf degil)
70        } catch (IllegalArgumentException e) {
71            System.out.println("Validasyon yakalandi: " + e.getMessage());
72        }
73        System.out.println();
74
75        // -------------------------------------------------------------------
76        // BOLUM 3: GERCEK HAYAT - Kullanici DTO + API response modeli
77        // -------------------------------------------------------------------
78        System.out.println("--- BOLUM 3: Gercek hayat - DTO ve API response ---");
79
80        KullaniciDTO kullanici = new KullaniciDTO(
81                42L, "ayse.yilmaz", "ayse@example.com", true);
82        System.out.println("Kullanici DTO: " + kullanici);
83        System.out.println("Aktif mi?    : " + kullanici.aktif());
84
85        // Record icinde ek (turetilmis) metot da tanimlanabilir:
86        System.out.println("Maskelenmis email: " + kullanici.maskeliEmail());
87
88        // Generic record ile tipli API response:
89        ApiResponse<KullaniciDTO> response =
90                new ApiResponse<>(200, "OK", kullanici);
91        System.out.println("API response : " + response);
92        System.out.println("Basarili mi? : " + response.basarili());
93        System.out.println();
94
95        // -------------------------------------------------------------------
96        // BOLUM 4: Record'larin gucu - Map anahtari ve metottan coklu deger
97        // -------------------------------------------------------------------
98        System.out.println("--- BOLUM 4: Record'lar Map anahtari olarak ---");
99        // Otomatik equals/hashCode sayesinde record'lar dogrudan
100        // HashMap anahtari olarak GUVENLE kullanilabilir.
101        Map<Nokta, String> sehirler = Map.of(
102                new Nokta(41, 28), "Istanbul",
103                new Nokta(39, 32), "Ankara"
104        );
105        // Yeni ama ayni degerli bir Nokta ile arama yapsak da bulur:
106        System.out.println("(41,28) -> " + sehirler.get(new Nokta(41, 28)));
107
108        // Metottan birden fazla deger dondurmek icin record (tuple yerine):
109        MinMax mm = minMaxBul(List.of(7, 2, 9, 4, 1, 8));
110        System.out.println("Min/Max: " + mm + " -> min=" + mm.min() + " max=" + mm.max());
111    }
112
113    /** Listenin min ve max degerini TEK donus degerinde tasiyan yardimci. */
114    static MinMax minMaxBul(List<Integer> sayilar) {
115        int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE;
116        for (int s : sayilar) {
117            min = Math.min(min, s);
118            max = Math.max(max, s);
119        }
120        return new MinMax(min, max);
121    }
122
123    // =========================================================================
124    //  ESKI YOL: Klasik immutable POJO/DTO -- ~50 satir BOILERPLATE
125    //  (record olmadan bir 2D nokta yazmak icin tum bunlar gerekir)
126    // =========================================================================
127    static final class NoktaEski {
128        private final int x;
129        private final int y;
130
131        public NoktaEski(int x, int y) {
132            this.x = x;
133            this.y = y;
134        }
135
136        public int getX() { return x; }
137        public int getY() { return y; }
138
139        @Override
140        public boolean equals(Object o) {
141            if (this == o) return true;
142            if (o == null || getClass() != o.getClass()) return false;
143            NoktaEski that = (NoktaEski) o;
144            return x == that.x && y == that.y;
145        }
146
147        @Override
148        public int hashCode() {
149            return Objects.hash(x, y);
150        }
151
152        @Override
153        public String toString() {
154            return "NoktaEski{x=" + x + ", y=" + y + "}";
155        }
156    }
157
158    // =========================================================================
159    //  YENI YOL: Yukaridaki ~50 satirin HEPSININ karsiligi -> TEK SATIR
160    // =========================================================================
161    record Nokta(int x, int y) { }
162    //  -> private final int x, y
163    //  -> public Nokta(int x, int y) { ... }   (canonical constructor)
164    //  -> public int x(), public int y()       (accessor'lar)
165    //  -> equals / hashCode / toString          (deger tabanli)
166
167    // =========================================================================
168    //  COMPACT CONSTRUCTOR ile validasyon iceren record.
169    //  "Para(double tutar, String birim)" yazmadan, parametre listesi
170    //  olmayan kompakt yapilandirici icinde dogrulama yapilir; alanlar
171    //  atama gerektirmez (Java otomatik atar).
172    // =========================================================================
173    record Para(double tutar, String birim) {
174        Para {  // <-- compact constructor (parametre parantezi YOK)
175            if (tutar < 0) {
176                throw new IllegalArgumentException("Tutar negatif olamaz: " + tutar);
177            }
178            if (birim == null || birim.length() != 3) {
179                throw new IllegalArgumentException(
180                        "Para birimi 3 harfli ISO kodu olmali (orn. TRY, USD): " + birim);
181            }
182        }
183    }
184
185    // =========================================================================
186    //  GERCEK HAYAT: Kullanici DTO (REST API'de tasinan veri)
187    //  Record icinde ek/turetilmis metot tanimlanabilir.
188    // =========================================================================
189    record KullaniciDTO(long id, String kullaniciAdi, String email, boolean aktif) {
190        /** Email'i loglarda gizlemek icin maskeleyen turetilmis metot. */
191        String maskeliEmail() {
192            int at = email.indexOf('@');
193            if (at <= 1) return "***";
194            return email.charAt(0) + "***" + email.substring(at);
195        }
196    }
197
198    // =========================================================================
199    //  GERCEK HAYAT: Generic API response modeli
200    // =========================================================================
201    record ApiResponse<T>(int statusKodu, String mesaj, T veri) {
202        boolean basarili() {
203            return statusKodu >= 200 && statusKodu < 300;
204        }
205    }
206
207    // =========================================================================
208    //  Metottan birden fazla deger dondurmek icin kucuk record (tuple yerine)
209    // =========================================================================
210    record MinMax(int min, int max) { }
211}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

SwitchExpressionsKalici

çalıştırılabilir
SwitchExpressionsKalici.java
1/*
2 * =============================================================================
3 *  Java 14 - SWITCH EXPRESSIONS (ARTIK KALICI / STANDART)
4 *  JEP 361: Switch Expressions
5 * =============================================================================
6 *
7 *  PREVIEW -> KALICI EVRIMI (cok onemli):
8 *    - Java 12: Switch Expressions ILK preview olarak geldi.
9 *    - Java 13: 2. preview oldu ("yield" anahtar kelimesi eklendi).
10 *    - Java 14: ARTIK KALICI! Standart dilin parcasi.
11 *
12 *  Bu, su anlama gelir:
13 *    -> Java 14 ve sonrasinda switch expressions kullanmak icin
14 *       "--enable-preview" bayragina ARTIK GEREK YOKTUR.
15 *    -> Normal "javac Dosya.java" ile derlenir.
16 *
17 *  Derleme/Calistirma (Java 14+):
18 *    javac SwitchExpressionsKalici.java
19 *    java  SwitchExpressionsKalici
20 * =============================================================================
21 */
22public class SwitchExpressionsKalici {
23
24    // ---- Ornek enum'lar (gercek hayat senaryolari icin) ----
25    enum Gun { PAZARTESI, SALI, CARSAMBA, PERSEMBE, CUMA, CUMARTESI, PAZAR }
26
27    enum MusteriSegmenti { STANDART, GUMUS, ALTIN, PLATIN }
28
29    enum IslemTipi { PARA_CEKME, PARA_YATIRMA, HAVALE, EFT, DOVIZ_ALIM }
30
31    public static void main(String[] args) {
32        System.out.println("=== Java 14: Switch Expressions (KALICI) ===\n");
33
34        // -------------------------------------------------------------------
35        // 1) ESKI YOL vs YENI YOL: Gun -> "hafta ici mi?" bilgisi
36        // -------------------------------------------------------------------
37        // ESKI (klasik switch deyimi): degisken disarida tanimlanir,
38        // her case icinde elle atanir, "break" unutma riski vardir.
39        Gun bugun = Gun.CUMARTESI;
40
41        String eskiSonuc;                       // once tanimla
42        switch (bugun) {
43            case CUMARTESI:
44            case PAZAR:
45                eskiSonuc = "Hafta sonu";
46                break;                          // break unutulursa fall-through!
47            default:
48                eskiSonuc = "Hafta ici";
49                break;
50        }
51        System.out.println("ESKI switch deyimi -> " + eskiSonuc);
52
53        // YENI (switch EXPRESSION): dogrudan deger DONDURUR, break YOK,
54        // fall-through riski YOK. Coklu case virgulle yazilir.
55        String yeniSonuc = switch (bugun) {
56            case CUMARTESI, PAZAR -> "Hafta sonu";   // -> oku otomatik break saglar
57            default               -> "Hafta ici";
58        };
59        System.out.println("YENI switch expression -> " + yeniSonuc + "\n");
60
61        // -------------------------------------------------------------------
62        // 2) Deger dondurme + degiskene atama (ay -> gun sayisi)
63        // -------------------------------------------------------------------
64        int ay = 2; // Subat
65        int gunSayisi = switch (ay) {
66            case 1, 3, 5, 7, 8, 10, 12 -> 31;
67            case 4, 6, 9, 11           -> 30;
68            case 2                     -> 28;   // (artik yil ihmal edildi)
69            default -> throw new IllegalArgumentException("Gecersiz ay: " + ay);
70        };
71        System.out.println(ay + ". ayda " + gunSayisi + " gun var.\n");
72
73        // -------------------------------------------------------------------
74        // 3) "yield" ile cok satirli case blogu (deger dondurmek icin)
75        // -------------------------------------------------------------------
76        // Bir case'de birden fazla satir is yapip SONRA deger dondurmek
77        // gerekirse "{ ... yield deger; }" kullanilir.
78        Gun gun = Gun.CUMA;
79        int kalanIsGunu = switch (gun) {
80            case PAZARTESI -> 5;
81            case SALI      -> 4;
82            case CARSAMBA  -> 3;
83            case PERSEMBE  -> 2;
84            case CUMA -> {
85                // Cok satirli hesaplama yapilabilir...
86                System.out.println("  (Cuma gunu: hafta sonuna 1 gun kaldi)");
87                int sonuc = 1;
88                yield sonuc;            // yield ile deger dondur
89            }
90            case CUMARTESI, PAZAR -> 0;
91        };
92        System.out.println("Hafta sonuna kalan is gunu: " + kalanIsGunu + "\n");
93
94        // -------------------------------------------------------------------
95        // GERCEK HAYAT ORNEGI 1: E-ticaret indirim orani (musteri segmenti)
96        // -------------------------------------------------------------------
97        System.out.println("--- Gercek Hayat: E-ticaret indirim orani ---");
98        for (MusteriSegmenti s : MusteriSegmenti.values()) {
99            double indirim = indirimOrani(s);
100            System.out.printf("  %-8s -> %%%.0f indirim%n", s, indirim * 100);
101        }
102        System.out.println();
103
104        // -------------------------------------------------------------------
105        // GERCEK HAYAT ORNEGI 2: Bankacilik islem komisyonu
106        // -------------------------------------------------------------------
107        System.out.println("--- Gercek Hayat: Banka islem komisyonu (1000 TL islem) ---");
108        double tutar = 1000.0;
109        for (IslemTipi t : IslemTipi.values()) {
110            double komisyon = komisyonHesapla(t, tutar);
111            System.out.printf("  %-12s -> %.2f TL komisyon%n", t, komisyon);
112        }
113    }
114
115    /**
116     * Musteri segmentine gore indirim orani dondurur.
117     * Switch expression sayesinde tek satirda, fall-through riski olmadan.
118     */
119    static double indirimOrani(MusteriSegmenti segment) {
120        return switch (segment) {
121            case STANDART -> 0.00;   // %0
122            case GUMUS    -> 0.05;   // %5
123            case ALTIN    -> 0.10;   // %10
124            case PLATIN   -> 0.20;   // %20
125        };
126        // NOT: enum'in tum degerleri kapsanmissa "default" gerekmez;
127        // derleyici "exhaustiveness" (tamlik) kontrolu yapar.
128    }
129
130    /**
131     * Islem tipine ve tutara gore komisyon hesaplar.
132     * Cok satirli mantik gerektiren case'lerde "yield" kullanilir.
133     */
134    static double komisyonHesapla(IslemTipi tip, double tutar) {
135        return switch (tip) {
136            case PARA_YATIRMA -> 0.0;                 // ucretsiz
137            case PARA_CEKME   -> 2.0;                 // sabit ucret
138            case HAVALE       -> tutar * 0.001;       // binde 1
139            case EFT          -> {
140                double oran = tutar * 0.002;          // binde 2
141                double minUcret = 3.0;                // en az 3 TL
142                yield Math.max(oran, minUcret);
143            }
144            case DOVIZ_ALIM   -> tutar * 0.005;       // binde 5
145        };
146    }
147}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.