Lambda fonksiyonlarının isimsiz sınıflara göre avantajı daha kısa ve öz olmalarıdır. Java bizlere fonksiyon nesneleri tanımlamak için kullanılabilecek lambdadan daha kısa bir yol sunmaktadır: metot referansları. Şimdi elimizde belli anahtarları Integer
değerlerine eşleştiren bir map
olsun. Bu Integer
değerler anahtarın veri yapısına kaç kere eklendiğini tutmaktadır. Yani ekleme işlemi yapılırken veri yapısına daha önceden hiç eklenmemiş bir anahtar için değer 1
olurken, daha önce eklenmiş anahtarlar için var olan değer artırılmaktadır.
map.merge(key, 1, (count, incr) -> count + incr);
Bu kodun Java 8 ile dile eklenen merge
metodunu kullandığını gözden kaçırmayın. Verilen anahtar (key) için daha önce eklenmiş bir eşleşme yok ise anahtar ikinci parametrede verilen 1
değeri ile map
içine eklenmektedir. Eğer daha önceden eklenmişse de anahtara karşılık gelen mevcut değerle 1
değeri üçüncü parametrede belirtilen fonksiyona sokulmakta ve çıkan sonuç mevcut değerin üzerine yazılmaktadır. Parametre olarak geçilen fonksiyon kendisine verilen parametreleri toplayıp döndürdüğü için, mevcut değer 1
artırılmış olacaktır. merge
metodunun temel kullanımı bu şekildedir.
Bu kısacık kod çok iş yapıyor olsa bile yine de gereksiz kısımlar vardır. count
ve incr
parametreleri pek bir iş yapmıyorlar ve boşuna alan kaplıyorlar. Burada lambda fonksiyonun tek yaptığı iş parametrelerini toplayıp döndürmektir, parametrelerin isimlerinin bir önemi yoktur. Java 8’den itibaren Integer
ve diğer kutulanmış temel türler (boxed primitive type) aynı işi yapan statik bir sum
metodu sağlamaktadır. Yeniden bir lambda tanımlamaktansa bu metodun referansını geçerek kodu daha da kısaltabiliriz.
map.merge(key, 1, Integer::sum);
Metot parametrelerinin sayısı arttıkça metot referansı kullanarak ortadan kaldırdığınız gereksiz kod miktarı artacaktır. Ancak bazı lambdalarda seçtiğiniz parametre isimleri okunabilirliği artırıyorsa için daha uzun olsa bile tercih edilebilir.
Metot referansları ile yapıp ile lambda ile yapamayacağınız hiçbir şey yoktur. (bunun pek de önemli olmayan bir istisnası vardır, merak edenler için JLS, 9.9-2) Metot referansları genellikle daha temiz ve kısa kod yazmanızı sağlarlar. Ayrıca lambda fonksiyon kodlarının uzadığı durumlarda size bir çıkış yolu sunarlar. Bu durumda lambda kodunu bir metoda taşıyabilir ve bu metodun referansını lambda fonksiyonu ile değiştirebilirsiniz. Metoda da istediğiniz bir isim verip güzelce dokümantasyon yapabilirsiniz.
IDE ile çalışıyorsanız birçok durumda size lambda fonksiyonunu metot referansı ile değiştirmenizi önerecek kabul ederseniz de bunu otomatik olarak yapacaktır. Metot referansları genellikle daha kısa ve anlaşılır oldukları için bu öneriyi çoğu zaman kabul edebilirsiniz. Ancak aşağıdaki gibi bir metot referansını düşünelim:
service.execute(GoshThisClassNameIsHumongous::action);
Yazdığımız bu kod satırı da action
metodu gibi GoshThisClassNameIsHumongous
sınıfının içindeyse lambda versiyonu aşağıdaki gibi olacaktır:
service.execute(() -> action());
Gördüğünüz gibi bu durumda metot referansı ne daha kısadır ne de daha anlaşılırdır. Bu sebeple lambda versiyonunu kullanmak daha mantıklı olacaktır. Benzer şekilde Function
arayüzünde birim fonksiyon (identity function) döndürmek için identity
adında bir statik fabrika metodu vardır. Bu metodun referansını kullanmaktansa x -> x
şeklinde daha kısa ve temiz olan lambda versiyonunu kullanmak daha mantıklı olabilir.
Birçok metot referansı statik metotlara işaret eder. Ancak bundan başka dört çeşit metot referansı daha vardır. Bunlardan iki tanesi bağlı (bound) ve serbest (unbound) metot referanslarıdır. Bağlı referanslarda metodun hangi nesne üzerinden çağrılacağı metot referansında belirtilir. Bunlar esasen statik referanslara benzerler, fonksiyon nesnesi ile işaret edilen metot aynı argümanlara sahiptir. Serbest referanslarda ise metodun hangi nesne üzerinden çağrılacağı metot referansında belli değildir, fonksiyon çalıştırıldığında belirtilir. Bunlar daha çok eşleştirme ve filtreleme işlemleri için stream yapılarıyla kullanılır. (Madde 45) Diğer iki tür ise sınıflar ve diziler için yapıcı metot referanslarıdır. Yapıcı metot referansları fabrika nesneleri gibi davranırlar. Aşağıdaki tabloda bütün bu beş farklı metot referansı özetlenmektedir:
Metot Referans Türü | Örnek | Lambda Karşılığı |
---|---|---|
Statik | Integer::parseInt | str -> Integer.parseInt(str) |
Bağlı (Bound) | Instant.now()::isAfter | Instant then = Instant.now(); t -> then.isAfter(t) |
Serbest (Unbound) | String::toLowerCase | str -> str.toLowerCase() |
Sınıf yapıcı metodu | TreeMap<K,V>::new | () -> new TreeMap<K,V> |
Dizi yapıcı metodu | int[]::new | len -> new int[len] |
Özetle, metot referansları fonksiyon nesneleri yaratmak için lambdaların yanında ikinci bir seçenek olarak karşımıza çıkarlar. Daha kısa ve okunabilir olduğu durumlarda metot referanslarını kullanın, aksi taktirde lambda kullanmaya devam edin.