Saya menjelajahi sumber Java 8 dan menemukan bagian kode ini sangat mengejutkan:
//defined in IntPipeline.java
@Override
public final OptionalInt reduce(IntBinaryOperator op) {
return evaluate(ReduceOps.makeInt(op));
}
@Override
public final OptionalInt max() {
return reduce(Math::max); //this is the gotcha line
}
//defined in Math.java
public static int max(int a, int b) {
return (a >= b) ? a : b;
}
Apakah Math::max
sesuatu seperti penunjuk metode? Bagaimana cara static
metode normal dikonversi IntBinaryOperator
?
TestingLambda$$Lambda$2/8460669
danTestingLambda$$Lambda$3/11043253
dibuat di dua doa.Jawaban:
Biasanya, seseorang akan memanggil
reduce
metode menggunakanMath.max(int, int)
sebagai berikut:Itu membutuhkan banyak sintaks untuk menelepon
Math.max
. Di situlah ekspresi lambda ikut bermain. Karena Java 8 diizinkan untuk melakukan hal yang sama dengan cara yang jauh lebih pendek:Bagaimana cara kerjanya? Kompiler java "mendeteksi", bahwa Anda ingin menerapkan metode yang menerima dua
int
dan mengembalikan satuint
. Ini sama dengan parameter formal dari satu-satunya metode antarmukaIntBinaryOperator
(parameter metodereduce
yang ingin Anda panggil). Jadi kompiler melakukan sisanya untuk Anda - itu hanya mengasumsikan Anda ingin menerapkanIntBinaryOperator
.Tetapi karena
Math.max(int, int)
memenuhi persyaratan formalIntBinaryOperator
, itu dapat digunakan secara langsung. Karena Java 7 tidak memiliki sintaks yang memungkinkan metode itu sendiri untuk diteruskan sebagai argumen (Anda hanya bisa melewati hasil metode, tetapi tidak pernah referensi metode),::
sintaks diperkenalkan di Java 8 untuk metode referensi:Perhatikan bahwa ini akan ditafsirkan oleh kompiler, bukan oleh JVM saat runtime! Walaupun ia menghasilkan bytecode yang berbeda untuk ketiga snipet kode, mereka secara semantik sama, sehingga dua yang terakhir dapat dianggap sebagai versi pendek (dan mungkin lebih efisien) dari
IntBinaryOperator
implementasi di atas!(Lihat juga Terjemahan dari Ekspresi Lambda )
sumber
::
disebut Referensi Metode. Ini pada dasarnya adalah referensi ke metode tunggal. Yaitu mengacu pada metode yang ada dengan nama.Penjelasan Singkat :
Di bawah ini adalah contoh referensi ke metode statis:
square
dapat diedarkan seperti referensi objek dan dipicu saat diperlukan. Bahkan, itu bisa dengan mudah digunakan sebagai referensi untuk metode objek "normal"static
. Sebagai contoh:Function
di atas adalah antarmuka fungsional . Untuk memahami sepenuhnya::
, penting untuk memahami antarmuka fungsional juga. Jelas, antarmuka fungsional adalah antarmuka dengan hanya satu metode abstrak.Contoh antarmuka fungsional meliputi
Runnable
,Callable
, danActionListener
.Function
di atas adalah antarmuka fungsional dengan hanya satu metode:apply
. Dibutuhkan satu argumen dan menghasilkan suatu hasil.Alasan mengapa
::
s mengagumkan adalah bahwa :Misalnya alih-alih menulis badan lambda
Anda cukup melakukannya
Saat runtime, kedua
square
metode ini berperilaku sama persis satu sama lain. Bytecode mungkin atau mungkin tidak sama (walaupun, untuk kasus di atas, bytecode yang sama dihasilkan; kompilasi di atas dan periksa denganjavap -c
).Satu-satunya kriteria utama yang harus dipenuhi adalah: metode yang Anda berikan harus memiliki tanda tangan yang mirip dengan metode antarmuka fungsional yang Anda gunakan sebagai referensi objek .
Di bawah ini ilegal:
square
mengharapkan argumen dan mengembalikan adouble
. Theget
metode dalam Pemasok mengembalikan nilai tetapi tidak mengambil argumen. Dengan demikian, ini menghasilkan kesalahan.Referensi metode mengacu pada metode antarmuka fungsional. (Seperti yang disebutkan, antarmuka fungsional masing-masing hanya dapat memiliki satu metode).
Beberapa contoh lagi:
accept
metode di Consumer mengambil input tetapi tidak mengembalikan apa pun.Di atas,
getRandom
tidak mengambil argumen dan mengembalikan adouble
. Jadi setiap antarmuka fungsional yang memenuhi kriteria: take no argumen and returndouble
dapat digunakan.Contoh lain:
Dalam hal tipe parameter :
Referensi metode dapat memiliki gaya yang berbeda, tetapi pada dasarnya mereka semua memiliki makna yang sama dan hanya dapat divisualisasikan sebagai lambda:
ClassName::methName
)instanceRef::methName
)super::methName
)ClassName::methName
)ClassName::new
)TypeName[]::new
)Untuk referensi lebih lanjut, lihat http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html .
sumber
Ya itu benar. The
::
operator yang digunakan untuk metode referensi. Jadi, seseorang dapat mengekstrak metode statis dari kelas dengan menggunakannya atau metode dari objek. Operator yang sama dapat digunakan bahkan untuk konstruktor. Semua kasus yang disebutkan di sini dicontohkan dalam contoh kode di bawah ini.Dokumentasi resmi dari Oracle dapat ditemukan di sini .
Anda dapat memiliki gambaran umum yang lebih baik tentang perubahan JDK 8 di artikel ini . Di bagian Metode / Konstruktor referensi contoh kode juga disediakan:
sumber
method(Math::max);
adalah doa dan definisi metode akan sepertipublic static void method(IntBinaryOperator op){System.out.println(op.applyAsInt(1, 2));}
. Begitulah cara menggunakannya.Sepertinya sedikit terlambat tetapi ini adalah dua sen saya. Sebuah ekspresi lambda digunakan untuk membuat metode anonim. Itu tidak melakukan apa-apa selain memanggil metode yang sudah ada, tetapi lebih jelas untuk merujuk ke metode langsung dengan namanya. Dan referensi metode memungkinkan kita untuk melakukannya menggunakan operator referensi-metode
::
.Pertimbangkan kelas sederhana berikut di mana setiap karyawan memiliki nama dan nilai.
Misalkan kita memiliki daftar karyawan yang dikembalikan dengan beberapa metode dan kami ingin menyortir karyawan berdasarkan peringkat mereka. Kami tahu kami dapat menggunakan kelas anonim sebagai:
di mana getDummyEmployee () adalah beberapa metode seperti:
Sekarang kita tahu bahwa Pembanding adalah Antarmuka Fungsional. Sebuah Antarmuka Fungsional adalah satu dengan tepat satu metode abstrak (meskipun mungkin mengandung satu atau lebih standar atau statis metode). Ekspresi Lambda menyediakan implementasi
@FunctionalInterface
sehingga antarmuka fungsional hanya dapat memiliki satu metode abstrak. Kita dapat menggunakan ekspresi lambda sebagai:Tampaknya semua baik tetapi bagaimana jika kelas
Employee
juga menyediakan metode serupa:Dalam hal ini menggunakan nama metode itu sendiri akan lebih jelas. Karenanya kita dapat langsung merujuk ke metode dengan menggunakan referensi metode sebagai:
Sesuai dokumen ada empat jenis referensi metode:
sumber
::
adalah operator baru yang termasuk dalam Java 8 yang digunakan untuk merujuk metode dari kelas yang ada. Anda bisa merujuk metode statis dan metode non-statis suatu kelas.Untuk merujuk metode statis, sintaksnya adalah:
Untuk merujuk metode non-statis, sintaksnya adalah
Dan
Satu-satunya prasyarat untuk merujuk metode adalah metode itu ada di antarmuka fungsional, yang harus kompatibel dengan referensi metode.
Referensi metode, ketika dievaluasi, membuat instance antarmuka fungsional.
Ditemukan di: http://www.speakingcs.com/2014/08/method-references-in-java-8.html
sumber
Ini adalah referensi metode di Java 8. Dokumentasi oracle ada di sini .
Sebagaimana dinyatakan dalam dokumentasi ...
sumber
:: Operator diperkenalkan di java 8 untuk referensi metode. Referensi metode adalah sintaks steno untuk ekspresi lambda yang mengeksekusi hanya satu metode. Berikut sintaks umum referensi metode:
Kami tahu bahwa kami dapat menggunakannya ekspresi lambda alih-alih menggunakan kelas anonim. Tetapi terkadang, ekspresi lambda benar-benar hanya panggilan ke beberapa metode, misalnya:
Untuk membuat kode lebih jelas, Anda bisa mengubah ekspresi lambda menjadi referensi metode:
sumber
:: dikenal sebagai referensi metode. Katakanlah kita ingin memanggil metode CalculPrice dari Pembelian kelas. Maka kita dapat menuliskannya sebagai:
Ini juga dapat dilihat sebagai bentuk singkat penulisan ekspresi lambda Karena referensi metode diubah menjadi ekspresi lambda.
sumber
Saya menemukan sumber ini sangat menarik.
Bahkan, itu adalah Lambda yang berubah menjadi Double Colon . Double Colon lebih mudah dibaca. Kami mengikuti langkah-langkah itu:
LANGKAH 1:
LANGKAH 2:
LANGKAH3:
sumber
Person::getAge()
harusPerson::getAge
.return reduce(Math::max);
adalah TIDAK SAMA untukreturn reduce(max());
Tapi artinya, kira-kira seperti ini:
Anda hanya dapat menyimpan 47 penekanan tombol jika Anda menulis seperti ini
sumber
Karena banyak jawaban di sini menjelaskan
::
perilaku yang baik , saya juga ingin menjelaskan bahwa::
operator tidak perlu memiliki tanda tangan yang sama persis dengan Fungsional Interface yang merujuk jika digunakan untuk variabel instan . Mari kita asumsikan kita membutuhkan BinaryOperator yang memiliki tipe TestObject . Secara tradisional ini diterapkan seperti ini:Seperti yang Anda lihat dalam implementasi anonim itu membutuhkan dua argumen TestObject dan mengembalikan objek TestObject juga. Untuk memenuhi kondisi ini dengan menggunakan
::
operator kita dapat mulai dengan metode statis:lalu panggil:
Ok itu dikompilasi dengan baik. Bagaimana jika kita membutuhkan metode instan? Mari perbarui TestObject dengan metode instan:
Sekarang kita dapat mengakses instance seperti di bawah ini:
Kode ini dikompilasi dengan baik, tetapi di bawahnya tidak:
Gerhana saya memberi tahu saya "Tidak dapat membuat referensi statis ke metode pengujian non-statisInstance (TestObject, TestObject) dari jenis TestObject ..."
Cukup adil, ini adalah metode instan, tetapi jika kita kelebihan
testInstance
seperti di bawah ini:Dan telepon:
Kode hanya akan dikompilasi dengan baik. Karena itu akan memanggil
testInstance
dengan parameter tunggal alih-alih ganda. Ok jadi apa yang terjadi dua parameter kita? Mari cetak dan lihat:Yang akan menghasilkan:
Ok jadi JVM cukup pintar untuk memanggil param1.testInstance (param2). Bisakah kita menggunakan
testInstance
dari sumber lain tetapi bukan dari TestObject, yaitu:Dan telepon:
Itu tidak akan dikompilasi dan kompiler akan memberi tahu: "Jenis TestUtil tidak mendefinisikan testInstance (TestObject, TestObject)" . Jadi kompiler akan mencari referensi statis jika bukan tipe yang sama. Ok bagaimana dengan polimorfisme? Jika kami menghapus pengubah akhir dan menambahkan kelas SubTestObject kami :
Dan telepon:
Ini tidak akan dikompilasi juga, kompiler akan tetap mencari referensi statis. Tetapi kode di bawah ini akan dikompilasi dengan baik karena ia lulus is-a test:
sumber
Dalam java-8 Streams Reducer dalam karya sederhana adalah fungsi yang mengambil dua nilai sebagai input dan mengembalikan hasil setelah beberapa perhitungan. hasil ini dimasukkan dalam iterasi berikutnya.
dalam hal Matematika: fungsi maks, metode terus mengembalikan maks dari dua nilai yang diteruskan dan pada akhirnya Anda memiliki angka terbesar di tangan.
sumber
Pada saat runtime mereka berperilaku sama persis. Bytecode mungkin / tidak sama (Untuk Incase di atas, ia menghasilkan bytecode yang sama (sesuai di atas dan periksa javaap -c;))
Saat runtime mereka berperilaku sama persis. Metode (matematika :: maks) ;, menghasilkan matematika yang sama (sesuai di atas dan periksa javap -c;))
sumber
Dalam versi Java yang lebih lama, alih-alih "::" atau lambd, Anda dapat menggunakan:
Atau meneruskan ke metode:
sumber
Jadi saya melihat di sini banyak jawaban yang terlalu rumit, dan itu meremehkan.
Jawabannya cukup sederhana: :: itu disebut Referensi Metode https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
Jadi saya tidak akan menyalin-menempel, pada tautan, Anda dapat menemukan semua informasi jika Anda menggulir ke bawah ke tabel.
Sekarang, mari kita lihat apa itu Referensi Metode:
A :: B agak mengganti ekspresi lambda inline berikut : (params ...) -> AB (params ...)
Untuk menghubungkan ini dengan pertanyaan Anda, Anda perlu memahami ekspresi java lambda. Itu tidak sulit.
Ekspresi lambda inline mirip dengan antarmuka fungsional yang ditentukan (yang merupakan antarmuka yang memiliki tidak lebih dan tidak kurang dari 1 metode) . Mari kita lihat apa yang saya maksud:
InterfaceX harus berupa antarmuka fungsional. Antarmuka fungsional apa pun, satu-satunya hal yang penting tentang InterfaceX untuk kompiler itu adalah Anda menentukan formatnya:
InterfaceX dapat berupa semua ini:
atau ini
atau lebih umum:
Mari kita ambil contoh kasus pertama dan ekspresi lambda sebaris yang telah kita definisikan sebelumnya.
Sebelum Java 8, Anda bisa mendefinisikannya dengan cara ini:
Secara fungsional, itu hal yang sama. Perbedaannya lebih pada bagaimana kompiler memahami ini.
Sekarang kita melihat ekspresi lambda sebaris, mari kita kembali ke Referensi Metode (: :). Katakanlah Anda memiliki kelas seperti ini:
Karena metode anyFunctions memiliki tipe yang sama dengan InterfaceX callMe , kita dapat menyamakan keduanya dengan Referensi Metode.
Kita bisa menulis seperti ini:
dan itu setara dengan ini:
Suatu hal yang keren dan keuntungan dari Referensi Metode adalah bahwa pada awalnya, sampai Anda menetapkannya ke variabel, mereka tidak ada artinya. Jadi, Anda dapat mengirimkannya sebagai parameter ke antarmuka fungsional yang tampak setara (memiliki tipe yang sama). Itulah tepatnya yang terjadi dalam kasus Anda
sumber
Jawaban sebelumnya cukup lengkap mengenai apa yang
::
dilakukan referensi metode. Singkatnya, ini menyediakan cara untuk merujuk ke metode (atau konstruktor) tanpa mengeksekusi itu, dan ketika dievaluasi, itu menciptakan sebuah instance dari antarmuka fungsional yang menyediakan konteks tipe target.Di bawah ini adalah dua contoh untuk menemukan objek dengan nilai maksimum dalam
ArrayList
DENGAN dan TANPA menggunakan::
referensi metode. Penjelasannya ada di komentar di bawah ini.TANPA penggunaan
::
DENGAN penggunaan
::
sumber
Double colon yaitu
::
operator diperkenalkan di Java 8 sebagai referensi metode . Referensi metode adalah bentuk ekspresi lambda yang digunakan untuk merujuk metode yang ada dengan namanya.classname :: methodName
ex:-
stream.forEach(element -> System.out.println(element))
Dengan menggunakan Double Colon
::
stream.forEach(System.out::println(element))
sumber