Ders İçeriği
Java, sürekli gelişen ve yeni özellikler eklenen dinamik bir dildir. Özellikle Java 8 ve sonraki sürümlerle birlikte dile eklenen lambda ifadeleri, Stream API ve modül sistemi gibi özellikler, modern Java geliştirmenin vazgeçilmez parçaları haline gelmiştir. Bu bölümde, bu ileri seviye konuları detaylı bir şekilde inceleyeceğiz.
1. Lambda İfadeleri (Lambda Expressions)
Lambda ifadeleri, Java 8 ile birlikte gelen ve fonksiyonel programlamayı Java'ya taşıyan önemli bir özelliktir. Tek bir soyut metodu olan arayüzlerin (fonksiyonel arayüzler) daha kısa ve okunabilir bir şekilde temsil edilmesini sağlarlar. Özellikle olay dinleyicileri, karşılaştırıcılar ve geri çağırma (callback) fonksiyonları gibi durumlarda kodun sadeleşmesine yardımcı olurlar.
Sözdizimi: (parametreler) -> ifade
veya (parametreler) -> { kod bloğu }
Örnek:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class LambdaOrnek {
public static void main(String[] args) {
List<String> isimler = new ArrayList<>();
isimler.add("Ayşe");
isimler.add("Ahmet");
isimler.add("Zeynep");
isimler.add("Mehmet");
// Geleneksel anonim sınıf ile sıralama
Collections.sort(isimler, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
System.out.println("Geleneksel sıralama: " + isimler); // [Ahmet, Ayşe, Mehmet, Zeynep]
// Lambda ifadesi ile sıralama
Collections.sort(isimler, (s1, s2) -> s2.compareTo(s1)); // Ters sıralama
System.out.println("Lambda ile ters sıralama: " + isimler); // [Zeynep, Mehmet, Ayşe, Ahmet]
// Foreach ile yazdırma
isimler.forEach(isim -> System.out.println("Merhaba " + isim));
// Metot referansı ile yazdırma (System.out::println)
isimler.forEach(System.out::println);
}
}
2. Stream API
Stream API, Java 8 ile gelen bir diğer güçlü özelliktir ve koleksiyonlar üzerinde fonksiyonel stil operasyonlar yapmayı sağlar. Veri üzerinde filtreleme, dönüştürme, gruplama gibi işlemleri daha okunabilir ve paralel olarak gerçekleştirmek için kullanılır. Streamler, veriyi doğrudan depolamazlar; bunun yerine, bir veri kaynağı (koleksiyon, dizi, I/O kanalı vb.) üzerinde işlem yaparlar.
Temel Kavramlar:
- Veri Kaynağı: Streamin üzerinde işlem yapacağı veri (Collection, Array, I/O).
- Ara İşlemler (Intermediate Operations): Bir Stream döndüren ve zincirlenebilen işlemlerdir (örn.
filter()
,map()
,sorted()
). Bu işlemler tembeldir (lazy), yani terminal işlem çağrılana kadar yürütülmezler. - Terminal İşlemler (Terminal Operations): Bir değer veya yan etki döndüren ve Stream pipeline'ını sonlandıran işlemlerdir (örn.
forEach()
,collect()
,reduce()
,count()
).
Örnek:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamApiOrnek {
public static void main(String[] args) {
List<Integer> sayilar = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Çift sayıları filtrele, ikiyle çarp ve yeni bir listeye topla
List<Integer> ciftSayilarinIkiKati = sayilar.stream()
.filter(n -> n % 2 == 0) // Ara işlem: çift sayıları filtrele
.map(n -> n * 2) // Ara işlem: her sayıyı ikiyle çarp
.collect(Collectors.toList()); // Terminal işlem: sonuçları listeye topla
System.out.println("Çift sayıların iki katı: " + ciftSayilarinIkiKati); // [4, 8, 12, 16, 20]
// Sayıların toplamını bul
int toplam = sayilar.stream()
.reduce(0, (a, b) -> a + b); // Terminal işlem: indirgeme (toplama)
System.out.println("Sayıların toplamı: " + toplam); // 55
// Belirli bir koşulu sağlayan eleman var mı?
boolean besVarMi = sayilar.stream().anyMatch(n -> n == 5);
System.out.println("Listede 5 var mı? " + besVarMi); // true
}
}
3. Java Modül Sistemi (JPMS - Java Platform Module System)
Java 9 ile birlikte gelen Java Platform Modül Sistemi (JPMS), Java uygulamalarının daha modüler, güvenli ve bakımı kolay olmasını sağlar. Uygulamaları ve JDK'yı modüllere ayırarak, sadece ihtiyaç duyulan kodun paketlenmesini ve dağıtılmasını mümkün kılar. Bu, uygulamanın boyutunu küçültür ve başlatma süresini hızlandırır.
Temel Kavramlar:
- Modül: İlgili paketlerin, kaynakların ve hizmetlerin adlandırılmış, kendi kendine yeten bir birimidir. Bir modül, hangi paketleri dışa aktardığını (export) ve hangi modüllere bağımlı olduğunu (requires) açıkça belirtir.
module-info.java
: Bir modülün tanımını içeren özel bir dosyadır. Modülün adını, dışa aktardığı paketleri ve bağımlılıklarını içerir.
Örnek module-info.java
:
// my.module modülünün tanımı
module my.module {
// Bu modül, com.example.api paketini dışa aktarır, böylece diğer modüller bu paketi kullanabilir.
exports com.example.api;
// Bu modül, java.sql modülüne bağımlıdır.
requires java.sql;
// Bu modül, com.example.service arayüzünün bir uygulamasını sağlar.
provides com.example.service.MyService with com.example.service.impl.MyServiceImpl;
// Bu modül, com.example.util paketini yansıma (reflection) yoluyla kullanıma açar.
opens com.example.util;
}
Modül Sisteminin Faydaları:
- Güvenilir Yapılandırma (Reliable Configuration): Modüller arasındaki bağımlılıklar derleme ve çalışma zamanında açıkça kontrol edilir, bu da eksik bağımlılık sorunlarını azaltır.
- Güçlü Kapsülleme (Strong Encapsulation): Modüller, dışa aktarılmayan paketlerin dışarıdan erişimini kısıtlar, bu da iç uygulama detaylarının gizlenmesini sağlar.
- Ölçeklenebilirlik: Büyük uygulamaların daha küçük, yönetilebilir modüllere ayrılmasını kolaylaştırır.
- Performans: Sadece gerekli modüllerin yüklenmesi, uygulamanın başlatma süresini ve bellek kullanımını azaltır.
4. Record Sınıfları (Java 16+)
Record sınıfları, Java 16 ile birlikte gelen ve veri taşıyıcı sınıflar (data carriers) için kısa ve öz bir sözdizimi sağlayan özel bir sınıf türüdür. Özellikle değişmez (immutable) veri nesneleri oluşturmak için idealdirler. equals()
, hashCode()
, toString()
metotlarını ve tüm alanlar için getter
metotlarını otomatik olarak oluştururlar.
Sözdizimi: record Isim(tip alan1, tip alan2, ...)
Örnek:
public record Ogrenci(String ad, String soyad, int numara) {
// Recordlar otomatik olarak constructor, getter, equals, hashCode, toString metotlarını oluşturur.
// İsteğe bağlı olarak ek metotlar veya kompakt constructor tanımlanabilir.
public String tamAd() {
return ad + " " + soyad;
}
}
public class RecordOrnek {
public static void main(String[] args) {
Ogrenci ogrenci1 = new Ogrenci("Ayşe", "Yılmaz", 101);
Ogrenci ogrenci2 = new Ogrenci("Ayşe", "Yılmaz", 101);
Ogrenci ogrenci3 = new Ogrenci("Ali", "Can", 102);
System.out.println(ogrenci1); // Ogrenci[ad=Ayşe, soyad=Yılmaz, numara=101]
System.out.println(ogrenci1.ad()); // Ayşe (getter metodu)
System.out.println(ogrenci1.tamAd()); // Ayşe Yılmaz (özel metot)
System.out.println("ogrenci1 equals ogrenci2: " + ogrenci1.equals(ogrenci2)); // true
System.out.println("ogrenci1 equals ogrenci3: " + ogrenci1.equals(ogrenci3)); // false
}
}
5. Pattern Matching for instanceof
(Java 16+)
Java 16 ile birlikte instanceof
operatörü için desen eşleştirme (pattern matching) özelliği eklenmiştir. Bu özellik, bir nesnenin belirli bir türde olup olmadığını kontrol ederken ve aynı zamanda o türe güvenli bir şekilde dönüştürürken kodu daha kısa ve okunabilir hale getirir.
Örnek:
public class PatternMatchingOrnek {
public static void main(String[] args) {
Object obj = "Merhaba Dünya!";
// Eski yöntem
if (obj instanceof String) {
String s = (String) obj;
System.out.println("Eski yöntem: " + s.length());
}
// Yeni yöntem (Pattern Matching)
if (obj instanceof String s) { // Eğer obj String ise, s değişkenine otomatik olarak atar
System.out.println("Yeni yöntem: " + s.length());
}
Object sayi = 123;
if (sayi instanceof Integer i && i > 100) {
System.out.println("Sayı 100'den büyük bir Integer: " + i);
}
}
}
Bu ileri seviye konular, Java geliştiricilerinin daha modern, verimli ve okunabilir kod yazmalarına olanak tanır. Java ekosistemi sürekli geliştiği için, bu yeni özellikleri takip etmek ve uygulamalarınıza entegre etmek önemlidir. Bu ders serisiyle Java öğrenme yolculuğunuzda önemli bir adım attınız. Başarılar dileriz!