Java8 Lambdas ve Anonim sınıflar


111

Java8 yakın zamanda piyasaya sürüldüğünden ve yepyeni lambda ifadeleri gerçekten harika göründüğünden, bunun alıştığımız Anonymous sınıflarının ölümü anlamına gelip gelmediğini merak ediyordum.

Bu konuda biraz araştırma yaptım ve Lambda ifadelerinin sistematik olarak bu sınıfların yerini nasıl alacağına dair harika örnekler buldum, örneğin Koleksiyonun sıralama yöntemi, sıralamayı gerçekleştirmek için Anonim bir Karşılaştırıcı örneği almak için kullanılır:

Collections.sort(personList, new Comparator<Person>(){
  public int compare(Person p1, Person p2){
    return p1.firstName.compareTo(p2.firstName);
  }
});

Artık Lambdas kullanılarak yapılabilir:

Collections.sort(personList, (Person p1, Person p2) -> p1.firstName.compareTo(p2.firstName));

Ve şaşırtıcı derecede özlü görünüyor. Öyleyse sorum şu, Lambdas yerine Java8'de bu sınıfları kullanmaya devam etmek için herhangi bir neden var mı?

DÜZENLE

Aynı soru ama tersi yönde, Anonim sınıflar yerine Lambdas kullanmanın faydaları nelerdir, çünkü Lambdas yalnızca tek yöntemli arayüzlerle kullanılabilir, bu yeni özellik yalnızca birkaç durumda kullanılan bir kısayol mu yoksa gerçekten yararlı mı?


5
Elbette, yan etkileri olan yöntemler sağlayan tüm bu anonim sınıflar için.
tobias_k

11
Sadece bilginiz için, karşılaştırıcıyı şu şekilde de oluşturabilirsiniz: Comparator.comparing(Person::getFirstName)eğer getFirstName()geri dönen bir yöntem olacaksa firstName.
skiwi

1
Veya birden çok yöntemi olan anonim sınıflar veya ...
Mark Rotteveel

1
Özellikle DÜZENLE'den sonraki ek sorular nedeniyle, çok geniş olduğu kadar yakın için oy verme eğilimindeyim .
Mark Rotteveel

1
Bu konuyla ilgili güzel ve derinlemesine bir makale: infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood
Ram Patra

Yanıtlar:


108

Soyut bir sınıfın veya somut bir sınıfın alt sınıfını oluşturmak için anonim bir iç sınıf (AIC) kullanılabilir. Bir AIC, durum (alanlar) dahil olmak üzere bir arayüzün somut bir uygulamasını da sağlayabilir. Bir AIC örneğine this, yöntem gövdelerinde kullanılarak başvurulabilir , böylece başka yöntemler çağrılabilir, durumu zaman içinde değiştirilebilir, vb. Bunların hiçbiri lambdalar için geçerli değildir.

AIC'lerin kullanımlarının çoğunun tekli işlevlerin durumsuz uygulamalarını sağladığını ve bu nedenle lambda ifadeleriyle değiştirilebileceğini tahmin ediyorum, ancak lambda'ların kullanılamadığı AIC'lerin başka kullanımları da var. AIC'ler burada kalacak.

GÜNCELLEME

AIC'ler ve lambda ifadeleri arasındaki diğer bir fark, AIC'lerin yeni bir kapsam getirmesidir. Diğer bir deyişle, adlar AIC'nin üst sınıflarından ve arabirimlerinden çözümlenir ve sözcüksel olarak çevreleyen ortamda oluşan adları gölgeleyebilir. Lambdas için tüm isimler sözcüksel olarak çözümlenir.


1
Lambdaların durumu olabilir. Bu açıdan, Lambdas ve AIC arasında bir fark görmüyorum.
nosid

1
@nosid AIC'ler, herhangi bir sınıfın örnekleri gibi, alanlarda durumu tutabilir ve bu durum, sınıfın herhangi bir yöntemi tarafından erişilebilir (ve potansiyel olarak değiştirilebilir). Bu durum, nesne GC'lenene kadar mevcuttur, yani belirsiz bir kapsamdadır, böylece yöntem çağrıları boyunca devam edebilir. Lambdaların sahip olduğu belirsiz ölçüde tek durum, lambda ile karşılaşıldığında yakalanır; bu durum değişmezdir. Bir lambda içindeki yerel değişkenler değiştirilebilir, ancak bunlar yalnızca bir lambda çağrısı devam ederken var olurlar.
Stuart Marks

1
@nosid Ah, tek öğeli dizi saldırısı. Sayacınızı birden çok iş parçacığından kullanmayı denemeyin. Yığın üzerinde bir şey ayıracaksanız ve onu bir lambda içinde yakalayacaksanız, bir AIC de kullanabilir ve doğrudan değiştirebileceğiniz bir alan ekleyebilirsiniz. Bu şekilde bir lambda kullanmak işe yarayabilir, ancak gerçek bir nesneyi kullanabildiğiniz zaman neden uğraşasınız ki?
Stuart Marks

2
AIC bunun gibi bir dosya oluşturacak, A$1.classancak Lambda oluşturmayacaktır . Bunu Fark'a ekleyebilir miyim?
Asif Mushtaq

2
@UnKnown Bu esas olarak bir uygulama meselesidir; AIC'ler ile lambdalar arasındaki bir programın nasıl programlandığını etkilemez, ki bu soru çoğunlukla bununla ilgilidir. Bir lambda ifadesinin, gibi bir ada sahip bir sınıf oluşturduğunu unutmayın LambdaClass$$Lambda$1/1078694789. Bununla birlikte, bu sınıf tarafından değil, lambda meta fabrikası tarafından anında üretilir javac, dolayısıyla karşılık gelen bir .classdosya yoktur . Yine de, bu bir uygulama endişesidir.
Stuart Marks

60

Lambdas harika bir özellik olsa da, yalnızca SAM türleriyle çalışacaktır. Yani, sadece tek bir soyut yöntemle arayüzler. Arayüzünüz birden fazla soyut yöntem içerdiğinde başarısız olur. Anonim sınıfların faydalı olacağı yer burasıdır.

Yani, hayır, sadece anonim sınıfları görmezden gelemeyiz. Bilginize, sort()yönteminiz p1ve için tür bildirimini atlayarak daha basitleştirilebilir p2:

Collections.sort(personList, (p1, p2) -> p1.firstName.compareTo(p2.firstName));

Ayrıca burada yöntem referansını da kullanabilirsiniz. Ya sınıfa bir compareByFirstName()yöntem eklersiniz Personve şunu kullanırsınız:

Collections.sort(personList, Person::compareByFirstName);

veya, bir alıcı eklemek firstNamedoğrudan almak Comparatorden Comparator.comparing()yöntemle:

Collections.sort(personList, Comparator.comparing(Person::getFirstName));

4
Biliyordum, ama okunabilirlik açısından uzun olanı tercih ediyorum, çünkü aksi takdirde bu değişkenlerin nereden geldiğini bulmak kafa karıştırıcı olabilir.
Amin Abu-Taleb

@ AminAbu-Taleb Neden kafa karıştırıcı olsun. Bu, geçerli bir Lambda sözdizimidir. Türler yine de çıkarılır. Her neyse, bu kişisel bir seçim. Türleri açık olarak verebilirsiniz. Sorun yok.
Rohit Jain

1
Anonim sınıfları doğrudan yeni şerh verilebilir: lambdas ve anonim sınıflar arasındaki başka ince bir fark vardır Java 8 Tip Ek Açıklamalar örn gibi new @MyTypeAnnotation SomeInterface(){};. Lambda ifadeleri için bu mümkün değildir. Ayrıntılar için şuradaki soruma bakın: Bir Lambda İfadesinin işlevsel arayüzüne açıklama ekleme .
Balder

36

Anonim sınıflarla Lambda performansı

Uygulama başlatıldığında, her sınıf dosyası yüklenmeli ve doğrulanmalıdır.

Anonim sınıflar, derleyici tarafından verilen sınıf veya arabirim için yeni bir alt tür olarak işlenir, böylece her biri için yeni bir sınıf dosyası oluşturulur.

Lambdalar bayt kodu oluşturmada farklıdır, daha verimlidirler, JDK7 ile birlikte gelen çağrılan dinamik talimatları kullanırlar.

Lambdas için bu komut, lambda ifadesini bayt kodunda çevirmeyi çalışma zamanına kadar geciktirmek için kullanılır. (talimat yalnızca ilk kez çağrılacaktır)

Sonuç olarak, Lambda ifadesi statik bir yöntem haline gelir (çalışma zamanında oluşturulur). (Stateles ve statefull case ile küçük bir fark vardır, bunlar üretilen yöntem argümanları aracılığıyla çözülür)


Her lambda ayrıca yeni bir sınıfa ihtiyaç duyar, ancak çalışma zamanında oluşturulur, bu nedenle bu anlamda lambdalar anonim sınıflardan daha verimli değildir. Lambdalar, yeni anonim sınıf örnekleri oluşturmak için kullanılandan invokedynamicgenellikle daha yavaş olan aracılığıyla oluşturulur invokespecial. Dolayısıyla, bu anlamda lambdalar da yavaştır (ancak, JVM invokedynamicçoğu zaman çağrıları optimize edebilir ).
ZhekaKozlov

3
@AndreiTomashpolskiy 1. Lütfen kibar olun. 2. Bu yorumu bir derleyici mühendisi tarafından okuyun: habrahabr.ru/post/313350/comments/#comment_9885460
ZhekaKozlov

@ZhekaKozlov, JRE kaynak kodunu okumak ve javap / hata ayıklayıcı kullanmak için derleyici mühendisi olmanıza gerek yok. Eksik olan şey, bir lambda yöntemi için bir sarmalayıcı sınıfının üretilmesinin tamamen bellek içinde yapıldığı ve bir AIC'nin başlatılması karşılık gelen sınıf kaynağının çözülmesini ve yüklenmesini içerir (bu, G / Ç sistem çağrısı anlamına gelir). Bu nedenle, invokedynamicgeçici sınıf oluşturma, derlenmiş anonim sınıflara kıyasla çok hızlıdır.
Andrei Tomashpolskiy

@AndreiTomashpolskiy G / Ç mutlaka yavaş değil
ZhekaKozlov

14

Aşağıdaki farklılıklar vardır:

1) Sözdizimi

Lambda ifadeleri, Anonim İç Sınıf (AIC) ile karşılaştırıldığında daha düzgün görünüyor

public static void main(String[] args) {
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("in run");
        }
    };

    Thread t = new Thread(r);
    t.start(); 
}

//syntax of lambda expression 
public static void main(String[] args) {
    Runnable r = ()->{System.out.println("in run");};
    Thread t = new Thread(r);
    t.start();
}

2) Kapsam

Anonim bir iç sınıf, bir sınıftır, yani iç sınıf içinde tanımlanmış değişken için kapsama sahiptir.

Oysa lambda ifadesi kendi kapsamı değildir, kapsama kapsamının bir parçasıdır.

Benzer kural, anonim iç sınıf ve lambda ifadesi içinde kullanıldığında süper ve bu anahtar kelime için de geçerlidir . Anonim iç sınıf olması durumunda, bu anahtar sözcük yerel kapsamı, süper anahtar sözcüğü ise anonim sınıfın süper sınıfını ifade eder. Lambda ifadesi durumunda bu anahtar sözcük, çevreleyen türdeki nesneyi belirtir ve super, çevreleyen sınıfın süper sınıfını ifade eder.

//AIC
    public static void main(String[] args) {
        final int cnt = 0; 
        Runnable r = new Runnable() {
            @Override
            public void run() {
                int cnt = 5;    
                System.out.println("in run" + cnt);
            }
        };

        Thread t = new Thread(r);
        t.start();
    }

//Lambda
    public static void main(String[] args) {
        final int cnt = 0; 
        Runnable r = ()->{
            int cnt = 5; //compilation error
            System.out.println("in run"+cnt);};
        Thread t = new Thread(r);
        t.start();
    }

3) Performans

Çalışma zamanında anonim iç sınıflar, sınıf yüklemesini, bellek ayırmayı ve statik olmayan bir yöntemin nesne başlatılmasını ve başlatılmasını gerektirirken, lambda ifadesi saf derleme zamanı etkinliğidir ve çalışma süresi sırasında ekstra maliyete neden olmaz. Bu nedenle lambda ifadesinin performansı, anonim iç sınıflara kıyasla daha iyidir. **

** Bu noktanın tamamen doğru olmadığının farkındayım. Ayrıntılar için lütfen aşağıdaki soruya bakın. Lambda ve anonim sınıf içi performans: ClassLoader üzerindeki yükü azaltmak mı?


3

Lambda'lar java 8'de işlevsel programlama için tanıtıldı. Ortak koddan kaçınabileceğiniz yer. Lambda'lar ile ilgili bu ilginç makaleye rastladım.

http://radar.oreilly.com/2014/04/whats-new-in-java-8-lambdas.html

Basit mantık için lambda işlevlerinin kullanılması önerilir. Lambdas kullanarak karmaşık mantık uygulamak, bir sorun olması durumunda kodda hata ayıklamada ek yük olacaktır.


0

Anonim sınıflar kalıcıdır çünkü lambda tek soyut yöntemlere sahip işlevler için iyidir, ancak diğer tüm durumlarda anonim iç sınıflar kurtarıcınızdır.


-1
  • lambda sözdizimi, java'nın çıkarabileceği bariz kodu yazmayı gerektirmez.
  • Kullanılarak invoke dynamic, lambda anonim sınıflara geri dönüştürülmez derleme sırasında (Java, nesneler oluşturmak zorunda değildir, sadece yöntemin imzasını önemsemek, nesne oluşturmadan yönteme bağlanabilir.
  • lambda, yapmadan önce yapmamız gereken şey yerine ne yapmak istediğimize daha fazla önem veriyor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.