Java 15 (Eylül 2020) — Detaylı Sürüm Analizi
> Bu doküman Java 15 sürümünü Türkçe olarak, her özellik için > NEDİR / NEDEN GELDİ / NE İŞE YARAR / NEREDE KOLAYLIK SAĞLAR / > ESKİ vs YENİ / GERÇEK HAYAT ÖRNEĞİ başlıklarıyla derinlemesine açıklar.
Java 15 (Eylül 2020) — Detaylı Sürüm Analizi
Bu doküman Java 15 sürümünü Türkçe olarak, her özellik için NEDİR / NEDEN GELDİ / NE İŞE YARAR / NEREDE KOLAYLIK SAĞLAR / ESKİ vs YENİ / GERÇEK HAYAT ÖRNEĞİ başlıklarıyla derinlemesine açıklar.
0. Sürüm Bilgisi (Genel Bakış)
| Bilgi | Değer |
|---|---|
| Sürüm | Java SE 15 / JDK 15 |
| Çıkış Tarihi | Eylül 2020 |
| Destek Türü | Non-LTS (kısa destekli ara sürüm) |
| Bir Önceki | Java 14 (Mart 2020) |
| Bir Sonraki | Java 16 (Mart 2021) |
Java 15, "feature release" (özellik sürümü) modelinin parçası olan 6 aylık ara sürümlerden biridir. LTS (uzun süreli destek) değildir — LTS sürümleri 8, 11, 17, 21'dir. Buna rağmen Java 15, dil tarihinin çok önemli bir kavşağıdır: Text Blocks burada kalıcı oldu ve Sealed Classes burada ilk kez göründü.
Özelliklerin Olgunluk (Preview → Kalıcı) Durumu
| Özellik | Java 13 | Java 14 | Java 15 | Java 16 | Java 17 |
|---|---|---|---|---|---|
| Text Blocks | Preview | Preview | KALICI ✅ | Kalıcı | Kalıcı |
| Records | — | 1. Preview | 2. Preview | Kalıcı ✅ | Kalıcı |
Pattern Matching for instanceof | — | 1. Preview | 2. Preview | Kalıcı ✅ | Kalıcı |
| Sealed Classes | — | — | 1. Preview ⭐ | 2. Preview | Kalıcı ✅ |
Önemli ayrım:
- Text Blocks: 13–14'te preview idi, 15'te KALICI oldu → artık
--enable-previewGEREKMEZ.- Sealed Classes: 15'te İLK kez (preview) geldi → 15/16'da
--enable-previewgerekir, 17'de kalıcı.- Records: 15'te İKİNCİ preview → 16'da kalıcı.
- Pattern Matching for
instanceof: 15'te İKİNCİ preview → 16'da kalıcı.
Bu Repodaki Çalışan Örnek Dosyalar
| Dosya | Konu |
|---|---|
TextBlocksKalici.java | Text Blocks (kalıcı) — JSON/SQL/HTML örnekleri |
SealedClassesPreview.java | Sealed Classes — Şekil ve Ödeme hiyerarşileri |
RecordSealedBirlikte.java | Records + Sealed birlikte — Cebirsel veri tipleri (ADT) |
1. Text Blocks — ARTIK KALICI (Standart) ✅
İlgili dosya: TextBlocksKalici.java
NEDİR
Üç çift tırnakla (""") başlayıp biten çok satırlı string yazma biçimidir.
JEP 378 ile Java 15'te kalıcılaştı.
NEDEN GELDİ (Hangi problemi çözüyor)
Java'da çok satırlı metin yazmak tarihsel olarak acı vericiydi:
- Her satır sonuna elle
\neklemek, - Satırları
+ile birleştirmek, - Metnin içindeki her
"karakterini\"olarak kaçırmak (özellikle JSON/HTML'de).
Buna topluca "kaçış karakteri cehennemi" denirdi. Kod, içerdiği metnin gerçek halini hiç andırmazdı; okunması ve bakımı zordu.
NE İŞE YARAR
Metni, bir editöre yazar gibi doğal, hizalı ve kaçışsız yazmamızı sağlar. Satır sonları otomatik eklenir, gereksiz girinti otomatik temizlenir.
NEREDE KOLAYLIK SAĞLAR
- JSON gövdeleri (REST istek/yanıt örnekleri, test verileri)
- SQL sorguları (çok satırlı, hizalı, okunaklı)
- HTML / XML şablonları (e-posta, rapor)
- JSON Schema, GraphQL, regex açıklamaları, çok satırlı loglar
ESKİ vs YENİ
// ESKİ (kaçış cehennemi) String json = "{\n" + " \"ad\": \"Ahmet\",\n" + " \"yas\": 30\n" + "}"; // YENİ (Java 15 — kalıcı Text Block) String json = """ { "ad": "Ahmet", "yas": 30 }""";
Özel davranışlar (dosyada örnekli)
- Otomatik girinti temizliği (incidental white space): en az girintili satıra
ve kapanış
"""konumuna göre soldaki ortak boşluk silinir. \(satır sonunda): bir sonraki satırla birleştirir (yeni satır eklemez).\s: tek bir boşluk; satır sonundaki boşlukların korunmasını sağlar.- Java 15 ayrıca
String.formatted(...),String.stripIndent(),String.translateEscapes()metotlarını da getirdi.
GERÇEK HAYAT ÖRNEĞİ
Bir e-ticaret uygulamasında sipariş onayı HTML e-postası üretmek. Eskiden
HTML şablonu Java string olarak yazmak imkansıza yakındı; Text Block + formatted
ile şablon, gerçek bir .html dosyası gibi okunur (bkz. ornek5_GercekHayat).
2. Sealed Classes (Mühürlü Sınıflar) — İLK PREVIEW ⭐
İlgili dosya: SealedClassesPreview.java
Sürüm: Java 15'te ilk preview (JEP 360). 15/16'da derlemek için
javac --release 15 --enable-preview ...gerekir. Java 17'de kalıcı oldu; 17+ ile bayraksız derlenir. (Dosyamız 17+ uyumlu yazıldı.)
NEDİR
Bir sınıf/arayüzün kendisini kimin extend/implement edebileceğini açıkça
sınırlandırmasıdır. Bu kontrol sealed ve permits anahtar kelimeleriyle yapılır.
Dört anahtar kelime:
sealed: "Mühürlüyüm; sadece izin verdiklerim alt tip olabilir."permits: İzin verilen alt tiplerin listesi.final: Alt tip kapatılır; ondan kimse türeyemez.non-sealed: Alt tip mührü açar; ondan herkes türeyebilir.
Kural: Mühürlü bir tipi extend eden her alt sınıf
final,sealedveyanon-sealedüçünden birini seçmek zorundadır.
NEDEN GELDİ (Hangi problemi çözüyor)
Eskiden bir hiyerarşiyi "yalnızca şu sınıflar genişletebilsin" diye kapatmanın temiz bir yolu yoktu:
final→ hiç kimse türetemez (çok katı).- package-private constructor hilesi → sadece aynı pakette türetilebilir; ama gerçek bir kontrol değil, niyeti belirsiz ve API tüketicisini şaşırtır.
- Hiçbir yöntem derleyiciye "bu hiyerarşi KAPALI ve alt tipler şunlardır" bilgisini veremiyordu → exhaustive (tüm durumları kapsayan) switch yapılamıyordu.
NE İŞE YARAR
- Alan modelini (domain) kasıtlı ve bilinen bir küme olarak modellemek.
- Derleyicinin tüm alt tipleri bilmesi sayesinde eksiksiz
switchyazmak; yeni bir alt tip eklenip de birswitchgüncellenmezse derleme hatası almak.
NEREDE KOLAYLIK SAĞLAR
- API kütüphaneleri: dışarıdan istenmeyen alt tip türetilmesini engelleme.
- Durum makineleri, AST/ifade ağaçları, ödeme/komut/olay türleri gibi sonlu seçenek kümeleri.
ESKİ vs YENİ
// ESKİ — niyet belirsiz, gerçek kontrol yok abstract class Sekil { Sekil() {} // package-private "hile" constructor } // YENİ — net ve derleyici denetimli sealed abstract class Sekil permits Daire, Kare, Ucgen {} final class Daire extends Sekil { ... } // kapalı final class Kare extends Sekil { ... } // kapalı non-sealed class Ucgen extends Sekil { ... } // bu dal kasıtlı açık
GERÇEK HAYAT ÖRNEĞİ
Ödeme türü hiyerarşisi. Bir ödeme sisteminde yöntemler sabit ve bilinen bir
küme olmalıdır: KrediKarti, Havale, Nakit. Üçüncü partinin sisteme
"BitcoinÖdeme" gibi keyfi bir tür eklemesi iş kuralları açısından tehlikelidir.
sealed interface Odeme permits ... ile bu küme mühürlenir; islemUcreti()
içindeki switch her durumu kapsamak zorunda kalır (bkz. odemeDemosu).
3. Records — İKİNCİ PREVIEW
İlgili dosya (kullanım): RecordSealedBirlikte.java
Sürüm: Java 14'te ilk preview, Java 15'te ikinci preview (JEP 384), Java 16'da kalıcı.
NEDİR
Yalnızca veri taşıyan, değişmez (immutable) sınıfları tek satırda tanımlamayı
sağlayan tip: record Nokta(int x, int y) {}. Derleyici otomatik olarak
constructor, alan erişimcileri, equals, hashCode ve toString üretir.
NEDEN GELDİ
Klasik "POJO/DTO" sınıfları onlarca satırlık tekrarlı kalıp kod (boilerplate)
gerektiriyordu: özel alanlar, getter'lar, equals/hashCode/toString. Bu kod
hem yorucuydu hem de hataya açıktı.
NE İŞE YARAR / NEREDE KOLAYLIK SAĞLAR
- DTO'lar, API yanıt/istek modelleri, değer nesneleri (value objects).
- Çoklu dönüş değerleri, harita anahtarları, kayıt ağaçları.
- 15'teki ikinci preview ile yerel (local) record'lar ve anotasyon davranışı iyileştirildi.
ESKİ vs YENİ
// ESKİ — ~30 satır boilerplate public final class Nokta { private final int x, y; public Nokta(int x, int y){ this.x=x; this.y=y; } public int x(){ return x; } public int y(){ return y; } // equals, hashCode, toString ... } // YENİ record Nokta(int x, int y) {}
GERÇEK HAYAT ÖRNEĞİ
İfade ağacındaki her düğüm (Sayi, Toplama, Carpma) tek satırlık record'tur;
immutable ve eşitliği otomatik (bkz. bölüm 5 ve RecordSealedBirlikte.java).
4. Pattern Matching for instanceof — İKİNCİ PREVIEW
İlgili kullanım: SealedClassesPreview.java içindeki aciklama(...) metodu.
Sürüm: Java 14'te ilk preview, Java 15'te ikinci preview (JEP 375), Java 16'da kalıcı.
NEDİR
instanceof kontrolünün, başarılı olduğunda otomatik olarak doğru tipte bir
değişken bağlamasıdır (binding variable).
NEDEN GELDİ
Klasik instanceof her zaman ardından manuel cast gerektiriyordu: önce
tip kontrol et, sonra aynı tipe elle dönüştür. Bu, tekrarlı ve hataya açıktı.
ESKİ vs YENİ
// ESKİ if (obj instanceof String) { String s = (String) obj; // manuel cast System.out.println(s.length()); } // YENİ if (obj instanceof String s) { // s otomatik bağlanır System.out.println(s.length()); }
NEREDE KOLAYLIK SAĞLAR
equals implementasyonları, tip ayrıştırma zincirleri ve (ileride) sealed +
record ile birleşince eksiksiz pattern matching switch'in temelini oluşturur.
5. Records + Sealed Birlikte — Cebirsel Veri Tipleri (ADT)
İlgili dosya: RecordSealedBirlikte.java
Sürüm notu: Java 15'te hem records (2. preview) hem sealed (1. preview) preview idi. O dönemde
--release 15 --enable-previewgerekiyordu. Java 17+ ile (sealed kalıcı, records kalıcı) bayraksız derlenir.
NEDİR / NEDEN GÜÇLÜ
Fonksiyonel dillerden gelen Cebirsel Veri Tipleri desenini Java'ya getirir:
sealed interface→ "bu tip şu seçeneklerden BİRİDİR" (sum / toplam tip)record→ "her seçenek şu alanlardan oluşur" (product / çarpım tip)pattern matching switch→ her seçeneği güvenle ayrıştırma
sealed interface Sekil permits Daire, Dikdortgen, Ucgen {} record Daire(double yaricap) implements Sekil {} record Dikdortgen(double en, double boy) implements Sekil {} record Ucgen(double taban, double yukseklik) implements Sekil {} static double alan(Sekil s) { return switch (s) { // 'default' GEREKMEZ — sealed sayesinde case Daire d -> Math.PI * d.yaricap() * d.yaricap(); case Dikdortgen d -> d.en() * d.boy(); case Ucgen u -> u.taban() * u.yukseklik() / 2.0; }; }
Üçlünün birleşimi modern Java'da şunu sağlar: az kod, değişmezlik, ve en
önemlisi derleyici garantili eksiksizlik. Yeni bir alt tip eklenip de switch
güncellenmezse derleme başarısız olur — yani "unutulan durum" hatası daha
çalıştırmadan, derleme aşamasında yakalanır.
GERÇEK HAYAT ÖRNEĞİ
İfade ağacı (Expression Tree) / hesap makinesi. Bir matematik ifadesi ya bir
sayıdır, ya iki ifadenin toplamı, ya çarpımıdır. (2 + 3) * 4 ifadesi
Carpma(Toplama(Sayi(2), Sayi(3)), Sayi(4)) olarak modellenir ve yinelemeli
hesapla(...) ile değerlendirilir (bkz. ifadeAgaciDemosu).
6. Sadece README'de Açıklanan Diğer Özellikler
6.1 Hidden Classes (Gizli Sınıflar) — JEP 371
- NEDİR: Çalışma zamanında (runtime) üretilen, normal sınıf yükleyiciler tarafından bulunamayan/keşfedilemeyen sınıflardır. Bytecode'dan dinamik olarak oluşturulur, başka kodlardan doğrudan referans verilemez.
- NEDEN GELDİ: Spring, Hibernate, mockito, dinamik proxy/lambda altyapıları
gibi framework'ler çalışma zamanında sürekli sınıf üretir. Eski yöntem
(
Unsafe.defineAnonymousClass) desteklenmeyen bir iç API idi. - NE İŞE YARAR: Framework'lere, çöp toplayıcı (GC) tarafından gerektiğinde boşaltılabilen, izole ve güvenli sınıf üretme imkanı verir. Bellek sızıntısı ve sınıf yükleyici şişmesini azaltır.
- NOT: Bu, son kullanıcı geliştirici için değil, çerçeve (framework) yazarları için bir altyapı özelliğidir; bu repoda kod örneği yoktur.
6.2 ZGC ve Shenandoah — ÜRETİME HAZIR (Production-Ready)
- ZGC (JEP 377): Düşük gecikmeli (çok kısa duraklamalı), terabaytlara kadar ölçeklenen çöp toplayıcı. Java 11'de deneysel gelmişti; Java 15'te üretime hazır ilan edildi.
- Shenandoah (JEP 379): Yine düşük duraklamalı GC; Java 12'de deneysel, Java 15'te üretime hazır.
- NE İŞE YARAR: GC duraklamalarının (pause) milisaniyeler değil mikrosaniyeler seviyesine inmesi gereken, büyük heap'li, gecikmeye duyarlı uygulamalarda (ticaret platformları, oyun sunucuları, büyük önbellekler).
- Kullanım:
java -XX:+UseZGC ...veyajava -XX:+UseShenandoahGC ...(artık-XX:+UnlockExperimentalVMOptionsgerekmez).
6.3 Nashorn JavaScript Engine — KALDIRILDI (JEP 372) ⚠️
- NEDİR: Nashorn, JVM içinde JavaScript çalıştıran motordu (Java 8'de gelmiş, Java 11'de "deprecated" işaretlenmişti). Java 15'te tamamen kaldırıldı.
- NEDEN: ECMAScript'in hızlı evrimine ayak uydurmak maliyetliydi; ekosistemde GraalVM JavaScript gibi daha güçlü alternatifler oluştu.
- MİGRASYON RİSKİ ⚠️:
jdk.nashorn.*paketlerini veyaScriptEngineManagerile"nashorn"motorunu kullanan kodlar Java 15'te ÇALIŞMAZ.jjskomut satırı aracı da kaldırıldı. Bu tür kodların GraalVM JavaScript veya başka bir script motoruna taşınması gerekir. Java 11'den 15'e geçişte en önemli "bozucu değişiklik"lerden (breaking change) biridir.
7. Java 14'ten Java 15'e Geçişte Ne Değişti?
| Konu | Java 14 | Java 15 |
|---|---|---|
| Text Blocks | Preview (bayrak gerekli) | Kalıcı (bayraksız) ✅ |
| Records | 1. Preview | 2. Preview (iyileştirildi) |
Pattern Matching instanceof | 1. Preview | 2. Preview |
| Sealed Classes | Yok | 1. Preview (yeni!) ⭐ |
| ZGC / Shenandoah | Deneysel | Üretime hazır |
| Nashorn | Deprecated | Kaldırıldı ⚠️ |
| Yeni String metotları | — | formatted, stripIndent, translateEscapes |
8. Avantajlar / Dezavantajlar / Riskler
✅ Avantajlar
- Text Blocks artık kalıcı: JSON/SQL/HTML içeren kod radikal biçimde okunaklı.
- Sealed Classes (ön izleme): Alan modelini güvenle kapatma; modern ADT'ye kapı.
- Records + Sealed + Pattern Matching birlikte: az kodla, derleyici garantili, eksiksiz (exhaustive) modelleme — Java'yı fonksiyonel dillere yaklaştırır.
- ZGC/Shenandoah üretime hazır: Büyük, gecikmeye duyarlı sistemler için ciddi performans seçeneği.
⚠️ Dezavantajlar
- Java 15 LTS değildir. Üretim için genelde 11 veya 17 (LTS) tercih edilir; 15 daha çok yeni özellikleri erken denemek içindir.
- Sealed/Records/Pattern Matching bu sürümde preview — API/sözdizimi sonraki sürümlerde değişebilir; preview kodu üretime almak risklidir.
- Preview özellikleri
--enable-previewbayrağı ve sürüm kilidi ile gelir; derlenen sınıflar yalnızca aynı JDK sürümünde çalışır.
🚨 Riskler (özellikle migrasyonda)
- Nashorn kaldırıldı:
jdk.nashorn.*,"nashorn"script motoru veyajjskullanan kodlar bozulur. Geçiş öncesi bağımlılık taraması şart. - Preview özellik kullanan kod, bir sonraki sürüme (16/17) geçerken sözdizimi/API değişiklikleri nedeniyle güncelleme gerektirebilir.
- 15 non-LTS olduğu için güvenlik güncellemeleri kısa sürelidir; uzun ömürlü projelerde doğrudan 15'e bağlanmak önerilmez.
9. Derleme ve Çalıştırma
# Text Blocks — Java 15+ (kalıcı, bayraksız) javac TextBlocksKalici.java java TextBlocksKalici # Sealed Classes — Java 15/16'da preview bayrağı gerekir: # javac --release 15 --enable-preview SealedClassesPreview.java # java --enable-preview SealedClassesPreview # Java 17+ ile bayraksız: javac SealedClassesPreview.java java SealedClassesPreview # Records + Sealed — Java 15'te ikisi de preview idi: # javac --release 15 --enable-preview RecordSealedBirlikte.java # java --enable-preview RecordSealedBirlikte # Java 17+ ile bayraksız (switch pattern matching için Java 21+ önerilir): javac RecordSealedBirlikte.java java RecordSealedBirlikte
Bu repodaki
.javadosyaları, modern bir JDK (17+, tercihen 21) ile preview bayrağı olmadan derlenip çalıştırılabilecek şekilde yazılmış ve test edilmiştir.
▶ Kod Örnekleri(3)
RecordSealedBirlikte
çalıştırılabilir1// =====================================================================================
2// RecordSealedBirlikte.java
3// Java 15 - Records + Sealed Classes BİRLİKTE (Cebirsel Veri Tipleri / ADT)
4// =====================================================================================
5//
6// ÖNEMLİ SÜRÜM NOTU:
7// ------------------
8// - Records: Java 14'te ilk preview, Java 15'te İKİNCİ preview (JEP 384),
9// Java 16'da KALICI (JEP 395).
10// - Sealed Classes: Java 15'te İLK preview (JEP 360), Java 17'de KALICI (JEP 409).
11//
12// Yani Java 15'te HEM records HEM sealed classes PREVIEW durumundaydı. O zaman
13// bu dosyayı derlemek için şu gerekiyordu:
14// javac --release 15 --enable-preview RecordSealedBirlikte.java
15// java --enable-preview RecordSealedBirlikte
16//
17// Java 17 ve sonrasında her ikisi de kalıcı olduğu için HİÇBİR BAYRAK gerekmez:
18// javac RecordSealedBirlikte.java
19// java RecordSealedBirlikte
20//
21// Bu dosya Java 17+ (ve switch pattern matching için Java 21+) ile derlenecek
22// şekilde yazılmıştır.
23//
24// =====================================================================================
25//
26// CEBİRSEL VERİ TİPLERİ (Algebraic Data Types - ADT) NEDİR?
27// ---------------------------------------------------------
28// Fonksiyonel dillerden (Haskell, Scala, Kotlin) gelen bir kavram. Bir tip,
29// "şunlardan BİRİ olabilir" şeklinde KAPALI bir seçenek kümesiyle tanımlanır:
30//
31// sealed interface -> "bu tip şu seçeneklerden biridir" (toplam / sum type)
32// record -> "her seçenek şu alanlardan oluşur" (çarpım / product type)
33//
34// Bu ikilinin Java 15 ile birleşmesi, Java'ya MODERN, GÜVENLİ ve KISA bir
35// modelleme aracı kazandırdı:
36// - sealed -> derleyici tüm olasılıkları bilir (exhaustive switch).
37// - record -> az kodla değişmez (immutable) veri taşıyıcıları.
38// - pattern matching -> her seçeneği tek tek, güvenle ayrıştırma.
39//
40// =====================================================================================
41
42public class RecordSealedBirlikte {
43
44 public static void main(String[] args) {
45 System.out.println("===== JAVA 15: RECORDS + SEALED (ADT) DEMOSU =====\n");
46 geometriDemosu();
47 ifadeAgaciDemosu();
48 }
49
50 // =====================================================================================
51 // ÖRNEK 1: GEOMETRİK ŞEKİLLER - sealed interface + record'lar
52 // =====================================================================================
53 //
54 // "Sekil" mühürlü bir arayüzdür ve SADECE Daire, Dikdortgen, Ucgen olabilir.
55 // Her seçenek bir record'dur: kısa, değişmez ve otomatik equals/hashCode/toString'li.
56 //
57 // Bu klasik ADT desenidir:
58 // sealed interface Sekil permits Daire, Dikdortgen, Ucgen
59 // record Daire(double yaricap) implements Sekil
60 // ...
61 //
62 // =====================================================================================
63
64 sealed interface Sekil permits Daire, Dikdortgen, Ucgen {}
65
66 record Daire(double yaricap) implements Sekil {}
67 record Dikdortgen(double en, double boy) implements Sekil {}
68 record Ucgen(double taban, double yukseklik) implements Sekil {}
69
70 // Alan hesabı: sealed + record + switch pattern matching birleşimi.
71 // 'Sekil' mühürlü olduğu için switch TÜM olasılıkları kapsar; 'default' GEREKMEZ.
72 // Eğer yarın 'permits' listesine yeni bir şekil eklenirse ama bu switch
73 // güncellenmezse, DERLEYİCİ HATA verir. İşte ADT'nin güvenlik gücü budur.
74 static double alan(Sekil s) {
75 return switch (s) {
76 // 'Record deconstruction' (kayıt ayrıştırma): record'un alanlarını
77 // doğrudan değişkenlere açıyoruz. (Java 21 record pattern özelliği.)
78 case Daire(double r) -> Math.PI * r * r;
79 case Dikdortgen(double en, double boy) -> en * boy;
80 case Ucgen(double t, double h) -> (t * h) / 2.0;
81 };
82 }
83
84 static void geometriDemosu() {
85 System.out.println("----- ÖRNEK 1: GEOMETRİK ŞEKİLLER (ADT) -----");
86
87 Sekil[] sekiller = {
88 new Daire(3),
89 new Dikdortgen(4, 5),
90 new Ucgen(6, 2)
91 };
92
93 for (Sekil s : sekiller) {
94 // record'ların otomatik toString'i sayesinde nesneler okunaklı yazılır.
95 System.out.printf("%-30s alan = %.2f%n", s, alan(s));
96 }
97 System.out.println();
98 System.out.println("Not: 'switch' içinde 'default' yok çünkü 'Sekil' mühürlü");
99 System.out.println("ve derleyici tüm alt tiplerin kapsandığını biliyor.");
100 System.out.println();
101 }
102
103
104 // =====================================================================================
105 // ÖRNEK 2: GERÇEK HAYAT - İFADE AĞACI (Expression Tree) ile HESAP MAKİNESİ
106 // =====================================================================================
107 //
108 // ADT'nin en klasik ve güçlü örneği: bir matematik ifadesini ağaç olarak modellemek.
109 // Bir "İfade" ya bir sayıdır, ya iki ifadenin toplamıdır, ya da çarpımıdır.
110 // Bu YİNELEMELİ (recursive) yapıyı sealed + record ile çok temiz ifade ederiz:
111 //
112 // sealed interface Ifade permits Sayi, Toplama, Carpma
113 // record Sayi(double deger) implements Ifade
114 // record Toplama(Ifade sol, Ifade sag) implements Ifade
115 // record Carpma(Ifade sol, Ifade sag) implements Ifade
116 //
117 // Örnek ifade: (2 + 3) * 4 -> Carpma(Toplama(Sayi(2), Sayi(3)), Sayi(4))
118 //
119 // =====================================================================================
120
121 sealed interface Ifade permits Sayi, Toplama, Carpma {}
122
123 record Sayi(double deger) implements Ifade {}
124 record Toplama(Ifade sol, Ifade sag) implements Ifade {}
125 record Carpma(Ifade sol, Ifade sag) implements Ifade {}
126
127 // İfadeyi yineleyerek (recursive) hesaplar. Her durumda record alanları ayrıştırılır.
128 static double hesapla(Ifade ifade) {
129 return switch (ifade) {
130 case Sayi(double d) -> d;
131 case Toplama(Ifade sol, Ifade sag) -> hesapla(sol) + hesapla(sag);
132 case Carpma(Ifade sol, Ifade sag) -> hesapla(sol) * hesapla(sag);
133 };
134 }
135
136 // İfadeyi insan-okur metne çevirir (parantezli). Yine sealed + pattern matching gücü.
137 static String yazdir(Ifade ifade) {
138 return switch (ifade) {
139 case Sayi(double d) -> (d == Math.floor(d)) ? String.valueOf((long) d) : String.valueOf(d);
140 case Toplama(Ifade sol, Ifade sag) -> "(" + yazdir(sol) + " + " + yazdir(sag) + ")";
141 case Carpma(Ifade sol, Ifade sag) -> "(" + yazdir(sol) + " * " + yazdir(sag) + ")";
142 };
143 }
144
145 static void ifadeAgaciDemosu() {
146 System.out.println("----- ÖRNEK 2: GERÇEK HAYAT - İFADE AĞACI -----");
147
148 // (2 + 3) * 4 = 20
149 Ifade ifade1 = new Carpma(
150 new Toplama(new Sayi(2), new Sayi(3)),
151 new Sayi(4)
152 );
153
154 // 10 + (5 * 6) = 40
155 Ifade ifade2 = new Toplama(
156 new Sayi(10),
157 new Carpma(new Sayi(5), new Sayi(6))
158 );
159
160 System.out.println(yazdir(ifade1) + " = " + hesapla(ifade1));
161 System.out.println(yazdir(ifade2) + " = " + hesapla(ifade2));
162
163 System.out.println();
164 System.out.println("NEDEN GÜÇLÜ?");
165 System.out.println("- sealed: 'Ifade' sadece Sayi/Toplama/Carpma olabilir (kapalı küme).");
166 System.out.println("- record: Her düğüm 1 satırda; immutable, eşitlik otomatik.");
167 System.out.println("- switch pattern matching: Her durumu güvenle, default'suz işleriz.");
168 System.out.println("- Yeni bir işlem (örn. Cikarma) eklenince derleyici tüm switch'leri");
169 System.out.println(" güncellememizi ZORUNLU kılar -> unutulan durum = derleme hatası.");
170 System.out.println("\n===== DEMO SONU =====");
171 }
172}SealedClassesPreview
çalıştırılabilir1// =====================================================================================
2// SealedClassesPreview.java
3// Java 15 - Sealed Classes (Mühürlü Sınıflar) - İLK PREVIEW (Önizleme)
4// =====================================================================================
5//
6// ÖNEMLİ SÜRÜM NOTU:
7// ------------------
8// - Sealed Classes ilk olarak Java 15'te (Eylül 2020, JEP 360) PREVIEW geldi.
9// - Java 16'da ikinci preview oldu (JEP 397).
10// - Java 17'de (Eylül 2021, JEP 409) KALICI / STANDART hale geldi.
11//
12// DERLEME:
13// - Java 15 veya 16'da derlerken/çalıştırırken preview bayrakları GEREKİR:
14// javac --release 15 --enable-preview SealedClassesPreview.java
15// java --enable-preview SealedClassesPreview
16// - Java 17 ve sonrasında HİÇBİR ÖZEL BAYRAK GEREKMEZ (kalıcı oldu):
17// javac SealedClassesPreview.java
18// java SealedClassesPreview
19//
20// Bu dosya Java 17+ ile preview bayrağı OLMADAN derlenebilecek şekilde yazılmıştır.
21//
22// =====================================================================================
23
24public class SealedClassesPreview {
25
26 public static void main(String[] args) {
27 System.out.println("===== JAVA 15 SEALED CLASSES (PREVIEW) DEMOSU =====\n");
28
29 sekilDemosu();
30 odemeDemosu();
31 }
32
33 // =====================================================================================
34 // SEALED CLASSES NEDİR? (Kavramsal açıklama)
35 // =====================================================================================
36 //
37 // "sealed" (mühürlü) bir sınıf veya arayüz, KENDİSİNİ KİMİN extend/implement
38 // EDEBİLECEĞİNİ açıkça belirler. Bunu "permits" anahtar kelimesiyle yapar.
39 //
40 // Anahtar kelimeler:
41 // - sealed : "Bu sınıf/arayüz mühürlüdür, sadece izin verdiklerim alt tip olabilir."
42 // - permits : "İzin verilen alt tipler şunlardır: ..."
43 // - final : Alt tip kapatılır, ondan da kimse türeyemez. (Hiyerarşi burada biter.)
44 // - non-sealed : Alt tip mührü AÇAR, artık herkes ondan türeyebilir. (Kontrolü gevşetir.)
45 //
46 // Bir mühürlü sınıfı extend eden HER alt sınıf, şu üçünden BİRİNİ seçmek ZORUNDADIR:
47 // final, sealed veya non-sealed.
48 //
49 // =====================================================================================
50
51 // -------------------------------------------------------------------------------------
52 // ESKİ DÜNYA (Java 14 ve öncesi) - Bir hiyerarşiyi kapatmak NEDEN zordu?
53 // -------------------------------------------------------------------------------------
54 // Bir Shape (Şekil) sınıfının SADECE Circle, Square, Triangle tarafından
55 // extend edilmesini istediğimizi düşünelim. Eski Java'da bunun temiz bir yolu YOKTU:
56 //
57 // YÖNTEM A) final yapmak -> Hiç kimse extend edemez. Ama biz BAZILARINA izin
58 // vermek istiyoruz; bu çok katı.
59 //
60 // YÖNTEM B) package-private constructor (görünür olmayan kurucu) hilesi ->
61 // Sınıfı sadece aynı pakette türetilebilir kılarsın. Ama:
62 // * Farklı paketten geliştirici "Neden extend edemiyorum?" diye şaşırır.
63 // * Aynı pakete sınıf ekleyen herkes yine de türetebilir (gerçek kontrol yok).
64 // * Derleyici "exhaustive" (tüm durumları kapsayan) switch yapamaz.
65 //
66 // YÖNTEM C) private constructor + iç içe statik sınıflar -> Çok hantal, esnek değil.
67 //
68 // SONUÇ: Eski yöntemlerin hiçbiri derleyiciye "bu hiyerarşi KAPALI ve alt tipler
69 // şunlardır" bilgisini veremiyordu. Sealed Classes tam olarak bunu çözer.
70 // -------------------------------------------------------------------------------------
71
72
73 // =====================================================================================
74 // ÖRNEK 1: Şekil (Sekil) Hiyerarşisi - SADECE Daire, Kare, Ucgen izinli
75 // =====================================================================================
76
77 // "sealed" + "permits": Sekil sadece bu üç sınıf tarafından genişletilebilir.
78 // Başka bir sınıf "extends Sekil" yazmaya çalışırsa DERLEME HATASI alır.
79 sealed static abstract class Sekil
80 permits Daire, Kare, Ucgen {
81 abstract double alan();
82 }
83
84 // "final": Daire hiyerarşinin yaprağıdır, ondan kimse türeyemez.
85 static final class Daire extends Sekil {
86 final double yaricap;
87 Daire(double yaricap) { this.yaricap = yaricap; }
88 double alan() { return Math.PI * yaricap * yaricap; }
89 }
90
91 // "final": Kare de kapalıdır.
92 static final class Kare extends Sekil {
93 final double kenar;
94 Kare(double kenar) { this.kenar = kenar; }
95 double alan() { return kenar * kenar; }
96 }
97
98 // "non-sealed": Ucgen mührü AÇAR. Yani isteyen herkes Ucgen'den türeyebilir
99 // (örn. Eskenar, Ikizkenar gibi). Bu, hiyerarşinin bir dalını kasıtlı açmak içindir.
100 static non-sealed class Ucgen extends Sekil {
101 final double taban, yukseklik;
102 Ucgen(double taban, double yukseklik) { this.taban = taban; this.yukseklik = yukseklik; }
103 double alan() { return (taban * yukseklik) / 2.0; }
104 }
105
106 // non-sealed olduğu için Ucgen'den serbestçe türetebiliyoruz:
107 static class Eskenar extends Ucgen {
108 Eskenar(double kenar) { super(kenar, kenar * Math.sqrt(3) / 2.0); }
109 }
110
111 static void sekilDemosu() {
112 System.out.println("----- ÖRNEK 1: ŞEKİL HİYERARŞİSİ -----");
113
114 Sekil[] sekiller = {
115 new Daire(5),
116 new Kare(4),
117 new Ucgen(6, 3),
118 new Eskenar(2)
119 };
120
121 for (Sekil s : sekiller) {
122 System.out.printf("%-10s alan = %.2f%n", aciklama(s), s.alan());
123 }
124 System.out.println();
125 }
126
127 // -------------------------------------------------------------------------------------
128 // SEALED + EXHAUSTIVE SWITCH'in GÜCÜ:
129 // Derleyici, Sekil'in TÜM alt tiplerini bildiği için, switch'in tüm durumları
130 // kapsadığını kontrol edebilir. (Java 21'de pattern matching switch ile birleşince
131 // "default" yazmaya bile gerek kalmaz; aşağıda klasik instanceof ile gösterildi
132 // ki Java 17'de de derlensin.)
133 // -------------------------------------------------------------------------------------
134 static String aciklama(Sekil s) {
135 // Java 16+ pattern matching for instanceof kullanıyoruz (Java 17'de kalıcı).
136 if (s instanceof Daire d) {
137 return "Daire(r=" + d.yaricap + ")";
138 } else if (s instanceof Kare k) {
139 return "Kare(a=" + k.kenar + ")";
140 } else if (s instanceof Ucgen u) {
141 return "Ucgen";
142 }
143 // sealed sayesinde teorik olarak buraya hiç gelinmez; yine de güvenlik için:
144 throw new IllegalStateException("Bilinmeyen şekil: " + s);
145 }
146
147
148 // =====================================================================================
149 // ÖRNEK 2: GERÇEK HAYAT - Ödeme Türü Hiyerarşisi
150 // =====================================================================================
151 // Bir ödeme sisteminde ödeme yöntemleri SABİT ve BİLİNEN bir küme olmalıdır:
152 // KrediKarti, Havale, Nakit. Dışarıdan yeni bir ödeme türü eklenmesi
153 // (örn. üçüncü partinin "BitcoinÖdeme" eklemesi) iş kuralları açısından TEHLİKELİDİR.
154 // Sealed ile bu kümeyi MÜHÜRLERİZ; sistem her zaman tüm olasılıkları bilir.
155 // =====================================================================================
156
157 sealed interface Odeme
158 permits KrediKarti, Havale, Nakit {
159 double tutar();
160 }
161
162 static record KrediKarti(double tutar, String kartNo) implements Odeme {}
163 static record Havale(double tutar, String iban) implements Odeme {}
164 static record Nakit(double tutar) implements Odeme {}
165
166 static void odemeDemosu() {
167 System.out.println("----- ÖRNEK 2: GERÇEK HAYAT - ÖDEME TÜRÜ -----");
168
169 Odeme[] odemeler = {
170 new KrediKarti(250.0, "1234-****-****-5678"),
171 new Havale(1000.0, "TR12 0001 0000 ..."),
172 new Nakit(75.50)
173 };
174
175 for (Odeme o : odemeler) {
176 System.out.printf("%-12s -> %s%n", o.getClass().getSimpleName(), islemUcreti(o));
177 }
178 System.out.println();
179 System.out.println("AVANTAJ: 'Odeme' mühürlü olduğu için ileride bir geliştirici");
180 System.out.println("yeni bir ödeme türü eklerse, islemUcreti() içindeki switch'i");
181 System.out.println("güncellemediği takdirde DERLEYİCİ uyarır/hata verir.");
182 System.out.println("Böylece 'unutulan durum' hatası daha derleme aşamasında yakalanır.");
183 }
184
185 // Mühürlü arayüz + pattern matching switch = exhaustive (tüm durumları kapsayan) kontrol.
186 // Tüm Odeme alt tipleri kapsandığı için derleyici 'default' istemez (Java 21).
187 static String islemUcreti(Odeme o) {
188 // switch ile pattern matching (Java 21 standardı). Daha eski sürümlerde
189 // bunu if-instanceof zinciriyle yazmak gerekirdi.
190 return switch (o) {
191 case KrediKarti k -> "Komisyon %2 -> " + (k.tutar() * 0.02) + " TL";
192 case Havale h -> "Sabit ücret 5 TL";
193 case Nakit n -> "Ücretsiz";
194 };
195 }
196}TextBlocksKalici
çalıştırılabilir1// =====================================================================================
2// TextBlocksKalici.java
3// Java 15 - Text Blocks (Metin Blokları) ARTIK KALICI (STANDART) ÖZELLİK
4// =====================================================================================
5//
6// ÖNEMLİ SÜRÜM NOTU:
7// ------------------
8// - Text Blocks ilk olarak Java 13'te PREVIEW (önizleme) olarak geldi (JEP 355).
9// - Java 14'te yine PREVIEW olarak devam etti (JEP 368, küçük iyileştirmelerle).
10// - Java 15'te (Eylül 2020, JEP 378) artık KALICI / STANDART hale geldi!
11//
12// Bunun pratik anlamı:
13// - Java 13 ve 14'te bu özelliği kullanmak için derlerken ve çalıştırırken
14// "--enable-preview" bayrağı GEREKİYORDU.
15// - Java 15 ve sonrasında HİÇBİR ÖZEL BAYRAK GEREKMİYOR. Tıpkı normal bir
16// String gibi doğrudan kullanılabilir.
17//
18// DERLEME (Java 15+):
19// javac TextBlocksKalici.java
20// java TextBlocksKalici
21// (Dikkat: --enable-preview YOK. Çünkü artık kalıcı bir dil özelliği.)
22//
23// =====================================================================================
24
25public class TextBlocksKalici {
26
27 public static void main(String[] args) {
28
29 System.out.println("===== JAVA 15 TEXT BLOCKS (KALICI) DEMOSU =====\n");
30
31 // -----------------------------------------------------------------------------
32 // 1) TEXT BLOCK NEDİR?
33 // -----------------------------------------------------------------------------
34 // Text Block, üç adet çift tırnakla (""") başlayıp biten, ÇOK SATIRLI bir
35 // string yazma biçimidir. Açılış """ işaretinden sonra MUTLAKA yeni satıra
36 // geçilmelidir. İçerikteki satır sonları (\n) otomatik olarak eklenir.
37 //
38 // ESKİDEN (klasik String) çok satırlı metin yazmak "kaçış karakteri cehennemi"
39 // demekti: her satır için \n, her tırnak için \" yazmak gerekiyordu.
40
41 ornek1_BasitKarsilastirma();
42 ornek2_JsonOrnegi();
43 ornek3_SqlOrnegi();
44 ornek4_OzelDavranislar();
45 ornek5_GercekHayat();
46 }
47
48 // =====================================================================================
49 // ÖRNEK 1: ESKİ vs YENİ - BASİT KARŞILAŞTIRMA
50 // =====================================================================================
51 static void ornek1_BasitKarsilastirma() {
52 System.out.println("----- ÖRNEK 1: ESKİ vs YENİ -----");
53
54 // ---- ESKİ YÖNTEM (Java 12 ve öncesi) - "kaçış karakteri cehennemi" ----
55 // Her satır sonunda \n, satırları birleştirmek için + operatörü.
56 String eskiYontem = "Merhaba Dünya!\n" +
57 "Bu metin\n" +
58 "birden fazla\n" +
59 "satırdan oluşuyor.\n";
60
61 // ---- YENİ YÖNTEM (Java 15 - kalıcı Text Block) ----
62 // Açılış """ işaretinden sonra yeni satıra geçildi. \n YOK, + YOK.
63 // Çok daha okunabilir ve metnin gerçek hali nasılsa kodda da öyle görünür.
64 String yeniYontem = """
65 Merhaba Dünya!
66 Bu metin
67 birden fazla
68 satırdan oluşuyor.
69 """;
70
71 System.out.println(">> Eski yöntem çıktısı:");
72 System.out.print(eskiYontem);
73 System.out.println(">> Yeni yöntem çıktısı:");
74 System.out.print(yeniYontem);
75
76 // İki yöntem aynı metni üretir mi? Evet!
77 System.out.println(">> İki metin eşit mi? " + eskiYontem.equals(yeniYontem));
78 System.out.println();
79 }
80
81 // =====================================================================================
82 // ÖRNEK 2: JSON YAZIMI - Text Block'un parladığı yer
83 // =====================================================================================
84 static void ornek2_JsonOrnegi() {
85 System.out.println("----- ÖRNEK 2: JSON ÖRNEĞİ -----");
86
87 // ---- ESKİ YÖNTEM: JSON içindeki her çift tırnak için \" yazmak zorundayız ----
88 String eskiJson = "{\n" +
89 " \"ad\": \"Ahmet\",\n" +
90 " \"soyad\": \"Yılmaz\",\n" +
91 " \"yas\": 30,\n" +
92 " \"adres\": {\n" +
93 " \"sehir\": \"İstanbul\",\n" +
94 " \"ulke\": \"Türkiye\"\n" +
95 " }\n" +
96 "}";
97
98 // ---- YENİ YÖNTEM: Çift tırnaklar olduğu gibi yazılır, kaçış gerekmez! ----
99 // JSON, kopyala-yapıştır ile doğrudan kullanılabilir hale gelir.
100 String yeniJson = """
101 {
102 "ad": "Ahmet",
103 "soyad": "Yılmaz",
104 "yas": 30,
105 "adres": {
106 "sehir": "İstanbul",
107 "ulke": "Türkiye"
108 }
109 }""";
110
111 System.out.println(">> Yeni Text Block ile JSON:");
112 System.out.println(yeniJson);
113 System.out.println();
114 System.out.println("(Eski yöntemde her \" karakterini \\\" olarak kaçırmak gerekirdi.)");
115 System.out.println();
116 }
117
118 // =====================================================================================
119 // ÖRNEK 3: SQL YAZIMI - Çok satırlı sorgular okunaklı olur
120 // =====================================================================================
121 static void ornek3_SqlOrnegi() {
122 System.out.println("----- ÖRNEK 3: SQL ÖRNEĞİ -----");
123
124 // ---- ESKİ YÖNTEM: + ile satır birleştirme, boşluklara dikkat etmek gerek ----
125 String eskiSql = "SELECT m.ad, m.soyad, d.ad AS departman " +
126 "FROM musteriler m " +
127 "INNER JOIN departmanlar d ON m.departman_id = d.id " +
128 "WHERE m.aktif = true " +
129 "ORDER BY m.soyad ASC";
130
131 // ---- YENİ YÖNTEM: Sorgu, bir SQL editöründeki gibi hizalı ve okunaklı ----
132 String yeniSql = """
133 SELECT m.ad, m.soyad, d.ad AS departman
134 FROM musteriler m
135 INNER JOIN departmanlar d ON m.departman_id = d.id
136 WHERE m.aktif = true
137 ORDER BY m.soyad ASC""";
138
139 System.out.println(">> Yeni Text Block ile SQL sorgusu:");
140 System.out.println(yeniSql);
141 System.out.println();
142 }
143
144 // =====================================================================================
145 // ÖRNEK 4: TEXT BLOCK ÖZEL DAVRANIŞLARI (incelik detayları)
146 // =====================================================================================
147 static void ornek4_OzelDavranislar() {
148 System.out.println("----- ÖRNEK 4: ÖZEL DAVRANIŞLAR -----");
149
150 // (a) "Incidental white space" (Tesadüfi boşluk) silinmesi:
151 // Java, en soldaki ortak girintiyi (en az girintili satıra göre) otomatik siler.
152 // Kapanış """ işaretinin konumu da girinti referansını etkiler.
153 String girintiOrnegi = """
154 Satır 1
155 Satır 2
156 """;
157 System.out.println(">> Girinti otomatik temizlenir:");
158 System.out.print(girintiOrnegi);
159
160 // (b) Satır sonu birleştirme: Satır sonunda \ kullanılırsa o satır,
161 // bir SONRAKİ satırla BİRLEŞTİRİLİR (yeni satır eklenmez).
162 // Çok uzun tek satırlık metinleri kodda bölmek için harikadır.
163 String tekSatir = """
164 Bu çok uzun bir cümle \
165 ama tek satır olarak \
166 birleştirilecek.""";
167 System.out.println(">> Satır sonu '\\' ile birleştirme:");
168 System.out.println(tekSatir);
169
170 // (c) Sonda boşluk korumak için \s kaçış dizisi (bir boşluk anlamına gelir):
171 String bosluk = """
172 Sonunda boşluk var:\s
173 İkinci satır""";
174 System.out.println(">> \\s ile sondaki boşluk korunur:");
175 System.out.println("[" + bosluk + "]");
176
177 // (d) Text Block normal bir String'dir; tüm String metotları çalışır.
178 String upper = """
179 küçük harf metin
180 """.toUpperCase();
181 System.out.println(">> Text Block üzerinde .toUpperCase(): " + upper);
182 System.out.println();
183 }
184
185 // =====================================================================================
186 // ÖRNEK 5: GERÇEK HAYAT ÖRNEĞİ - HTML E-Posta Şablonu Üretimi
187 // =====================================================================================
188 static void ornek5_GercekHayat() {
189 System.out.println("----- ÖRNEK 5: GERÇEK HAYAT - HTML E-POSTA ŞABLONU -----");
190
191 // Senaryo: Bir e-ticaret sistemi, sipariş onayı için HTML e-posta gönderiyor.
192 // Eskiden bu tür HTML şablonlarını Java string olarak yazmak korkunçtu
193 // (her tırnak kaçırılır, her satır +'lı, her \n elle eklenirdi).
194 // Text Block ile HTML, bir .html dosyasındaki gibi doğal görünür.
195
196 String musteriAdi = "Zeynep Kaya";
197 String siparisNo = "SP-2026-00891";
198 double tutar = 1499.90;
199
200 // Not: %s ve %.2f yer tutucularını String.formatted (Java 15'te String'e eklendi)
201 // ile dolduruyoruz. formatted(), String.format ile aynı işi yapar ama zincirlenebilir.
202 String htmlEposta = """
203 <html>
204 <body style="font-family: Arial, sans-serif;">
205 <h2>Sipariniz Alındı!</h2>
206 <p>Sayın <b>%s</b>,</p>
207 <p>Sipariş numaranız: <code>%s</code></p>
208 <p>Toplam tutar: <b>%.2f TL</b></p>
209 <hr/>
210 <p style="color: gray;">Bizi tercih ettiğiniz için teşekkürler.</p>
211 </body>
212 </html>
213 """.formatted(musteriAdi, siparisNo, tutar);
214
215 System.out.println(">> Üretilen HTML e-posta:");
216 System.out.println(htmlEposta);
217
218 System.out.println("AÇIKLAMA: Aynı HTML'i eski yöntemle yazsaydık, onlarca \\\" ve");
219 System.out.println("\\n ile dolu, okunması imkansız bir string elde ederdik.");
220 System.out.println("\n===== DEMO SONU =====");
221 }
222}