Java8 Lambdas vs kelas Anonim

111

Karena Java8 baru-baru ini dirilis dan ekspresi lambda barunya terlihat sangat keren, saya bertanya-tanya apakah ini berarti matinya kelas Anonymous yang dulu biasa kita alami.

Saya telah meneliti sedikit tentang ini dan menemukan beberapa contoh keren tentang bagaimana ekspresi Lambda akan secara sistematis menggantikan kelas-kelas tersebut, seperti metode sortir Collection, yang digunakan untuk mendapatkan contoh Anonymous dari Pembanding untuk melakukan pengurutan:

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

Sekarang dapat dilakukan menggunakan Lambdas:

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

Dan terlihat sangat ringkas. Jadi pertanyaan saya adalah, apakah ada alasan untuk tetap menggunakan kelas-kelas tersebut di Java8 daripada Lambdas?

EDIT

Pertanyaan yang sama tetapi dalam arah yang berlawanan, apa manfaat menggunakan Lambdas daripada kelas Anonim, karena Lambdas hanya dapat digunakan dengan antarmuka metode tunggal, apakah fitur baru ini hanya pintasan yang hanya digunakan dalam beberapa kasus atau apakah itu benar-benar berguna?

Amin Abu-Taleb
sumber
5
Tentu, untuk semua kelas anonim yang menyediakan metode dengan efek samping.
tobias_k
11
Hanya untuk info Anda, Anda juga dapat membuat pembanding sebagai:, Comparator.comparing(Person::getFirstName)jika getFirstName()akan menjadi metode yang kembali firstName.
skiwi
1
Atau kelas anonim dengan berbagai metode, atau ...
Mark Rotteveel
1
Saya tergoda untuk memilih mendekati sebagai terlalu luas, terutama karena pertanyaan tambahan setelah EDIT .
Mark Rotteveel
1
Artikel mendalam yang bagus tentang topik ini: infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood
Ram Patra

Jawaban:

108

Kelas dalam anonim (AIC) dapat digunakan untuk membuat subkelas dari kelas abstrak atau kelas konkret. AIC juga dapat memberikan implementasi konkret dari sebuah antarmuka, termasuk penambahan status (bidang). Sebuah instance dari AIC dapat dirujuk menggunakan thisdalam badan metodenya, sehingga metode lebih lanjut dapat dipanggil, statusnya dapat dimutasi dari waktu ke waktu, dll. Tak satu pun dari ini berlaku untuk lambda.

Saya menduga bahwa sebagian besar penggunaan AIC adalah untuk menyediakan implementasi stateless dari fungsi tunggal sehingga dapat diganti dengan ekspresi lambda, tetapi ada penggunaan AIC lain yang tidak dapat digunakan lambda. AIC akan tetap ada.

MEMPERBARUI

Perbedaan lain antara AIC dan ekspresi lambda adalah AIC memperkenalkan ruang lingkup baru. Yaitu, nama diselesaikan dari superkelas dan antarmuka AIC dan dapat membayangi nama yang muncul di lingkungan yang melingkupi secara leksikal. Untuk lambda, semua nama diselesaikan secara leksikal.

Stuart Marks
sumber
1
Lambdas dapat memiliki status. Dalam hal ini, saya tidak melihat perbedaan antara Lambdas dan AIC.
nosid
1
AIC @nosid, seperti instance kelas apa pun, dapat menyimpan status dalam bidang, dan status ini dapat diakses (dan berpotensi dapat berubah oleh) metode kelas apa pun. Keadaan ini ada sampai objek di-GC, yaitu, memiliki batas tak terbatas, sehingga bisa bertahan di seluruh pemanggilan metode. Satu-satunya keadaan dengan lambda yang tidak terbatas telah ditangkap pada saat lambda ditemukan; keadaan ini tidak bisa diubah. Variabel lokal dalam lambda bisa berubah, tetapi mereka ada hanya saat pemanggilan lambda sedang berlangsung.
Stuart Marks
1
@nosid Ah, peretasan array elemen tunggal. Hanya saja, jangan mencoba menggunakan penghitung Anda dari beberapa utas. Jika Anda akan mengalokasikan sesuatu di heap dan menangkapnya dalam lambda, Anda mungkin juga menggunakan AIC dan menambahkan bidang yang dapat Anda mutasi secara langsung. Menggunakan lambda dengan cara ini dapat berhasil, tetapi mengapa repot-repot ketika Anda dapat menggunakan objek nyata?
Stuart Marks
2
AIC akan membuat file seperti ini, A$1.classtetapi Lambda tidak. Bisakah saya menambahkan ini di Difference?
Asif Mushtaq
2
@UnKnown Itu terutama masalah implementasi; itu tidak mempengaruhi bagaimana satu program dengan AIC vs lambdas, yang sebagian besar tentang pertanyaan ini. Perhatikan bahwa ekspresi lambda menghasilkan kelas dengan nama seperti LambdaClass$$Lambda$1/1078694789. Namun, kelas ini dibuat dengan cepat oleh metafactory lambda, bukan oleh javac, jadi tidak ada .classfile yang sesuai . Namun, sekali lagi, ini adalah masalah implementasi.
Stuart Marks
60

Lambdas meskipun fitur hebat, hanya akan bekerja dengan jenis SAM. Artinya, antarmuka hanya dengan satu metode abstrak. Ini akan gagal segera setelah antarmuka Anda berisi lebih dari 1 metode abstrak. Di situlah kelas anonim akan berguna.

Jadi, tidak, kita tidak bisa begitu saja mengabaikan kelas anonim. Untuk diketahui, sort()metode Anda bisa lebih disederhanakan, dengan melewatkan deklarasi type for p1dan p2:

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

Anda juga dapat menggunakan referensi metode di sini. Entah Anda menambahkan compareByFirstName()metode di Personkelas, dan menggunakan:

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

atau, tambahkan getter for firstName, langsung dapatkan metode Comparatorfrom Comparator.comparing():

Collections.sort(personList, Comparator.comparing(Person::getFirstName));
Rohit Jain
sumber
4
Saya tahu, tapi saya lebih suka yang panjang dalam hal keterbacaan, karena jika tidak, akan membingungkan untuk mengetahui dari mana variabel ini berasal.
Amin Abu-Taleb
@ AminAbu-Taleb Mengapa ini membingungkan. Itu adalah sintaks Lambda yang valid. Jenisnya bagaimanapun juga disimpulkan. Bagaimanapun, itu pilihan pribadi. Anda bisa memberi tipe eksplisit. Tidak ada masalah.
Rohit Jain
1
Ada perbedaan halus lainnya antara lambda dan kelas anonim: kelas Anonymous bisa langsung dianotasi dengan Java 8 Type Annotations baru , seperti misalnya new @MyTypeAnnotation SomeInterface(){};. Ini tidak mungkin untuk ekspresi lambda. Untuk detailnya, lihat pertanyaan saya di sini: Menganotasi antarmuka fungsional dari Ekspresi Lambda .
Balder
36

Performa Lambda dengan kelas Anonymous

Saat aplikasi diluncurkan, setiap file kelas harus dimuat dan diverifikasi.

Kelas anonim diproses oleh kompilator sebagai subtipe baru untuk kelas atau antarmuka yang diberikan, sehingga akan dihasilkan file kelas baru untuk masing-masing kelas.

Lambda berbeda pada pembuatan bytecode, mereka lebih efisien, menggunakan instruksi dinamis yang dipanggil yang disertakan dengan JDK7.

Untuk Lambdas, instruksi ini digunakan untuk menunda menerjemahkan ekspresi lambda dalam bytecode hingga runtime. (instruksi akan dipanggil untuk pertama kali saja)

Akibatnya ekspresi Lambda akan menjadi metode statis (dibuat saat runtime). (Ada perbedaan kecil dengan kasus stateles dan statefull, mereka diselesaikan melalui argumen metode yang dihasilkan)

Dmitriy Kuzkin
sumber
Setiap lambda juga membutuhkan kelas baru, tetapi dibuat saat runtime, jadi dalam pengertian ini, lambda tidak lebih efisien daripada kelas anonim. Lambda dibuat invokedynamicyang umumnya lebih lambat daripada yang invokespecialdigunakan untuk membuat instance baru dari kelas anonim. Jadi, dalam hal ini lambda juga lebih lambat (namun, JVM dapat mengoptimalkan invokedynamicpanggilan hampir sepanjang waktu).
ZhekaKozlov
3
@AndreiTashpolskiy 1. Harap bersikap sopan. 2. Baca komentar ini oleh seorang insinyur penyusun: habrahabr.ru/post/313350/comments/#comment_9885460
ZhekaKozlov
@ZhekaKozlov, Anda tidak perlu menjadi insinyur kompiler untuk membaca kode sumber JRE dan menggunakan javap / debugger. Apa yang Anda lewatkan adalah pembuatan kelas pembungkus untuk metode lambda dilakukan sepenuhnya dalam memori dan hampir tidak memerlukan biaya, sementara membuat instance AIC melibatkan penyelesaian dan memuat sumber daya kelas yang sesuai (yang berarti panggilan sistem I / O). Oleh karena itu, invokedynamicdengan pembuatan kelas ad-hoc sangat cepat dibandingkan dengan kelas anonim yang dikompilasi.
Andrei Tomashpolskiy
@AndreiTomashpolskiy I / O tidak selalu lambat
ZhekaKozlov
14

Ada perbedaan sebagai berikut:

1) Sintaks

Ekspresi Lambda terlihat rapi dibandingkan dengan Anonymous Inner Class (AIC)

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) Ruang Lingkup

Kelas dalam anonim adalah kelas, yang berarti memiliki ruang lingkup untuk variabel yang ditentukan di dalam kelas dalam.

Padahal, ekspresi lambda bukanlah cakupannya sendiri, tetapi merupakan bagian dari cakupan yang melingkupi.

Aturan serupa berlaku untuk super dan kata kunci ini saat menggunakan di dalam kelas dalam anonim dan ekspresi lambda. Dalam kasus kelas dalam anonim, kata kunci ini mengacu pada cakupan lokal dan kata kunci super mengacu pada kelas super kelas anonim. Sementara dalam kasus ekspresi lambda kata kunci ini merujuk ke objek dari tipe penutup dan super akan merujuk ke kelas super kelas yang melingkupinya.

//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) Kinerja

Pada waktu proses, kelas dalam anonim memerlukan pemuatan kelas, alokasi memori, dan inisialisasi objek, serta pemanggilan metode non-statis, sedangkan ekspresi lambda adalah aktivitas waktu kompilasi murni dan tidak menimbulkan biaya tambahan selama waktu proses. Jadi kinerja ekspresi lambda lebih baik dibandingkan dengan kelas dalam anonim. **

** Saya menyadari bahwa poin ini tidak sepenuhnya benar. Silakan lihat pertanyaan berikut untuk detailnya. Kinerja kelas dalam Lambda vs anonim: mengurangi beban pada ClassLoader?

atom217
sumber
3

Lambda di java 8 diperkenalkan untuk pemrograman fungsional. Di mana Anda dapat menghindari kode boilerplate. Saya menemukan artikel menarik ini di lambda's.

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

Sebaiknya gunakan fungsi lambda untuk logika sederhana. Jika menerapkan logika kompleks menggunakan lambda akan menjadi overhead dalam men-debug kode jika terjadi masalah.

Tim
sumber
0

Kelas anonim tetap ada karena lambda bagus untuk fungsi dengan metode abstrak tunggal tetapi untuk semua kasus lain kelas dalam anonim adalah penyelamat Anda.

rapi
sumber
-1
  • sintaks lambda tidak perlu menulis kode jelas yang dapat disimpulkan oleh java.
  • Dengan menggunakan invoke dynamic, lambda tidak diubah kembali ke kelas anonim selama waktu kompilasi (Java tidak harus melalui pembuatan objek, cukup perhatikan tanda tangan metode, dapat mengikat ke metode tanpa membuat objek
  • lambda lebih menekankan pada apa yang ingin kita lakukan daripada apa yang harus kita lakukan sebelum kita dapat melakukannya
MagGGG
sumber