Ketik inferensi di Java 8

30

Apakah pengenalan notasi lambda baru (lihat misalnya artikel ini ) di Java 8 akan memerlukan beberapa jenis inferensi?

Jika demikian, bagaimana sistem tipe baru akan berdampak pada bahasa Java secara keseluruhan?

Giorgio
sumber

Jawaban:

47

Ada sedikit informasi yang salah dalam jawaban ratchet freak dan di utas komentarnya. Saya akan menjawab di sini dalam jawaban, karena komentar terlalu kecil. Juga, karena ini adalah jawaban, saya akan berusaha menjawab pertanyaan aslinya juga. (Namun perlu dicatat bahwa saya bukan ahli sistem tipe.)

Pertama, jawaban singkat untuk pertanyaan awal adalah Ya dan Tidak. Ya, Java 8 akan memiliki inferensi tipe yang jauh lebih banyak daripada Java 7, dan Tidak, tidak ada sistem tipe "baru" di Java 8, meskipun ada beberapa perubahan kecil .

Java 8 masih akan diketik secara statis, dan itu masih akan memiliki dikotomi antara kelas dan antarmuka. Tidak ada tipe baru seperti tipe fungsi. Jenis lambda pada dasarnya adalah "antarmuka fungsional" yang merupakan antarmuka biasa dengan metode abstrak tunggal.

Antarmuka sekarang dapat memiliki kode dalam bentuk metode default, tetapi model pewarisan tunggal kelas dan pewarisan berganda antarmuka tetap sama. Ada beberapa penyesuaian, tentu saja, seperti aturan untuk penyelesaian metode di hadapan metode default, tetapi fundamentalnya tidak berubah.

Tipe apa pun yang disimpulkan berdasarkan inferensi tipe dapat dituliskan secara eksplisit. Untuk menggunakan contoh ratchet freak ,

Collections.<MyClass>sort(list, (a, b) -> { return a.order - b.order; });

pada dasarnya gula untuk

Collections.<MyClass>sort(list,
    (Comparator<MyClass>)((MyClass a, MyClass b) -> { return a.order - b.order; }));

Jadi pernyataan sparkleshy "inferensi tipe tidak memerlukan ekstensi dari sistem tipe" pada dasarnya benar.

Tetapi untuk kembali ke gula sintaksis, saya akan mengulangi pernyataan saya bahwa ekspresi lambda bukanlah gula sintaksis untuk kelas batin anonim. Ratchet freak menyatakan bahwa ekspresi lambda diterjemahkan ke dalam instantiasi kelas dalam anonim, dan Sparkleshy hanya menegaskan kembali bahwa lambda adalah gula sintaksis untuk kelas dalam anonim, tetapi pernyataan ini tidak benar. Mereka mungkin didasarkan pada informasi yang sudah ketinggalan zaman. Implementasi lambda awal memang mengimplementasikan lambda dengan cara ini, tetapi banyak hal telah berubah.

Ekspresi Lambda secara semantik berbeda dari kelas dalam, dan mereka diimplementasikan secara berbeda dari kelas dalam.

Ekspresi Lambda secara semantik berbeda dari kelas dalam dalam beberapa cara. Mengevaluasi ekspresi lambda tidak perlu membuat instance baru setiap kali. Mereka juga memiliki semantik menangkap yang berbeda, misalnya, mereka menangkap ini secara berbeda. Dalam kelas batin, ini adalah instance kelas dalam, sedangkan dalam lambda, ini adalah contoh terlampir. Pertimbangkan yang berikut ini:

public class CaptureThis {
    void a(Runnable r) { r.run(); }

    void b() {
        a(new Runnable() { public void run() { System.out.println(this); }});
        a(() -> System.out.println(this));
    }

    public String toString() { return "outer"; }

    public static void main(String[] args) { new CaptureThis().b(); }
}

Dalam JDK 8 lambda build baru-baru ini (saya menggunakan b69 ), hasilnya akan seperti berikut:

CaptureThis$1@113de03
outer

Lebih jauh, ekspresi lambda diimplementasikan sepenuhnya berbeda dari kelas dalam. Jika Anda membandingkan keluaran yang dibongkar, Anda akan melihat bahwa kode kelas dalam langsung mengkompilasi ke penciptaan dan memanggil ke konstruktor dari CaptureThis $ 1, sedangkan ekspresi lambda mengkompilasi ke instruksi invokedynamic yang mendapatkan Runnable melalui cara yang tidak ditentukan. Untuk penjelasan lengkap tentang bagaimana ini bekerja dan mengapa, lihat Brian Goetz 'JavaOne 2012 bicara Lambda: A Peek Under The Hood .

Stuart Marks
sumber
2
"Mereka menangkap ini secara berbeda. Dalam kelas batin, ini adalah instance kelas dalam, sedangkan dalam lambda, ini adalah contoh terlampir. Pertimbangkan yang berikut:" masih dapat dilakukan dengan gula sintaksis dengan mengganti semua thisdenganMyClass.this
ratchet freak
4
Segala sesuatu yang melampaui assembler (dan kadang-kadang bahkan itu) adalah gula sintaksis. Tapi lambdas bukan gula sintaksis untuk kelas batin (lagi). Mereka melayani tujuan yang sama dan memiliki beberapa kesamaan, tetapi di bawah tenda mereka (berbeda).
Joachim Sauer
1
"Ekspresi Lambda secara semantik berbeda dari kelas dalam, dan mereka diimplementasikan secara berbeda dari kelas dalam.": Terima kasih atas penjelasannya yang sangat bagus (+1). Apa yang masih belum jelas bagi saya adalah mengapa lambdas tidak diimplementasikan sebagai gula sintaksis untuk kelas anonim khusus atau, dengan kata lain, apa keuntungan dari kompleksitas ekstra yang diperkenalkan oleh semantik baru? Masalah apa yang dipecahkan oleh masalah ini yang tidak dapat diselesaikan dengan kelas dalam anonim? (Tapi saya masih harus melihat tautan yang Anda poskan, mungkin saya akan menemukan jawabannya di sana.)
Giorgio
1
@ Joachim Sauer: Saya akan menganggap gula sintaksis sebagai sintaksis baru untuk semantik yang ada. Ketika semantik baru diperkenalkan, saya pikir Anda tidak dapat berbicara tentang gula sintaksis. Dalam hal ini, saya tidak berpikir bahwa Anda dapat berdebat bahwa segala sesuatu di luar assembler adalah gula sintaksis.
Giorgio
3
@Iorgio: tentang mengapa lambda bukan hanya gula sintaksis untuk kelas dalam anonim, ternyata ada diskusi besar tentang ini. Email dari Brian Goetz ini merangkum keputusan: mail.openjdk.java.net/pipermail/lambda-dev/2011-August/… . TL; DR itu membiarkan pintu terbuka untuk evolusi di masa depan dan kami mendapatkan kinerja yang lebih baik hari ini. Goetz sebenarnya menjawab pertanyaan terkait, Apakah objek lambdas? Tetapi jika jawabannya adalah TIDAK atau bahkan Mungkin itu menyiratkan mereka tidak bisa menjadi gula untuk kelas dalam.
Stuart Marks