Java'da Method İsimlerini Yakalamanın Akıllıca Yolu: SerializedLambda
Java'da Metot İsimlerini Çıkarmak: SerializedLambda ile Şık Bir Numara
CSV parser'lar, ORM framework'leri veya REST istemcileri ile çalışırken illa ki şu duvara toslamışsınızdır: bir alan adını string olarak geçirmeniz gerekiyor ama "author" veya "getBaslik" gibi sabit değerler yazmak içine sinmiyor. Ya yanlış yazarsın, ya da bir gün metot ismini değiştirdiğinde kırık linklerle baş başa kalırsın.
Peki ya şöyle yapabilseydiniz?
String yazar = csvIcerik.get(isimAl(Book::getYazar));
String baslik = csvIcerik.get(isimAl(Book::baslik));
Metot referansını doğrudan veriyorsunuz, gerisini Java hallediyor. Gelin bu işin nasıl çalıştığına bakalım.
Sorun Ne?
Java'da bir metot referansından metot ismini almak için doğrudan bir yol yok. Book::getYazar yazdığınızda elinizde Function<Book, String> nesnesi var, ama bu nesneye "Kardeşim, senin asıl metot ismin ne?" diye sorabileceğiniz bir API maalesef yok.
Reflection kullanarak sonradan bulmaya çalışmak mantıklı gelmiyor:
Method[] metotlar = Book.class.getMethods();
String isim = metotlar[0].getName(); // Çalışır da, ama hangisi?
Ama bu yaklaşımda zaten hangi metotu aradığınızı biliyor olmanız gerekiyor — yani bütün amacı yok ediyorsunuz.
SerializedLambda Sırrı
İşler burada ilginçleşiyor. Java bir lambda veya metot referansını serileştirirken, sadece anlamsız bir bytecode dizisi üretmiyor. Bunun yerine, orijinal metot hakkında meta veri içeren bir SerializedLambda nesnesi oluşturuyor — isim dahil.
Trick şu: bu serileştirme işlemi tamamlanmadan araya girip SerializedLambda nesnesini yakalamak.
İşte temel implementasyon:
public static <T, R> String isimAl(final Getter<T, R> metotRef) {
return serializedLambdaCevir(metotRef).getImplMethodName();
}
private static <T, R> SerializedLambda serializedLambdaCevir(final Object metotRef) {
try {
try (CustomObjectOutputStream akis = new CustomObjectOutputStream()) {
akis.writeObject(metotRef);
return (SerializedLambda) akis.arayaGirenNesne;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Burada replaceObject callback'ini kesen özel bir ObjectOutputStream yaratıyoruz. Java metot referansınızı serileştirmeye çalıştığında, SerializedLambda ile birlikte replaceObject'i çağırıyor — ve biz onu orada yakalıyoruz.
Neden Önemli?
Bu teknik bazı gerçekten kullanışlı kalıplar sağlıyor:
Veri işleme için tip-güvenli yapılandırma:
// Yerine:
dataMapper.esle(book, "baslik", "yazar");
// Şöyle yazabilirsiniz:
dataMapper.esle(book, isimAl(Book::baslik), isimAl(Book::getYazar));
API çağrıları için derleme zamanı güvenliği:
// Refactoring'e dayanıklı veritabanı sorguları
List<Book> sonuclar = kitapDeposu.bul(isimAl(Book::getYazar), "Shakespeare");
getYazar() metodunu yeniden adlandırdığınızda, derleyici isimAl(Book::getYazar) çağrılarını güncellemeniz gerektiği konusunda sizi uyaracak. Artık sabit stringleri peşinde koşturmaya gerek yok.
Basit Getter'ların Ötesinde
Temel kalıp sıfır argümanlı metodlar için harika çalışıyor, peki ya parametreli metodlar ne olacak? Yaklaşımı genişletmek için ek serileştirilebilir fonksiyonel arayüzler tanımlayabilirsiniz:
public interface CiftFonksiyon<T1, T2, R> extends Serializable {
R apply(T1 t1, T2 t2);
}
public <T1, T2, R> String isimAl(final CiftFonksiyon<T1, T2, R> metotRef) {
return serializedLambdaCevir(metotRef).getImplMethodName();
}
Bu sayede KitapServis::basligaVeYazaraGoreBul veya Depo::kaydet gibi metodları da handle edebilirsiniz.
Bir Uyarı Sözü
Bu teknik güçlü olsa da, aklınızda bulundurmanız gereken noktalar var:
- Performans: Serileştirme overhead getirir.
isimAl()'ı sıcak döngülerde çağırmayın — bir kere başlatma sırasında çağırıp sonucu önbelleğe alın. - Serileştirme gereksinimleri: Fonksiyonel arayüzlerinizin
Serializableimplement etmesi şart. - Evrensel değil: Bu sadece fonksiyonel arayüzlere atanabilir metot referansları için çalışıyor. Rastgele metodlar için hala doğrudan reflection gerekiyor.
Kütüphanelerİmdadımıza Yetişiyor
Bunu implement etmek size fazla boilerplate gibi geliyorsa, iyi haber: iş zaten yapılmış. Safety Mirror kütüphanesi sağlam implementasyonlar sunuyor ve hatta konsepti java.lang.reflect.Method nesnelerini doğrudan çıkarmak için genişletiyor — size tip güvenliği ile birlikte tam reflection yetenekleri sağlıyor.
Sonuç
Java'nın metot referansları güçlü, ama metot isimlerini kasıtlı olarak soyutluyorlar. Bazen bu meta verilere ihtiyacınız oluyor. SerializedLambda numarası bu boşluğu doldurmak için temiz, tip-güvenli bir yol sunuyor — sihirli stringlerden uzak, ifade gücü yüksek ve refactoring-dostu kod yazmanıza olanak tanıyor.
Veri işleme pipeline'ı mı kuruyorsunuz, esnek bir API istemcisi mi yazıyorsunuz, ya da sadece "getYazar" stringini kod tabanınızda kovalaktan bıktınız mı? Bu teknik tam aradığınız şey olabilir.