Saya punya pertanyaan tentang penggunaan Function.identity()
metode ini.
Bayangkan kode berikut:
Arrays.asList("a", "b", "c")
.stream()
.map(Function.identity()) // <- This,
.map(str -> str) // <- is the same as this.
.collect(Collectors.toMap(
Function.identity(), // <-- And this,
str -> str)); // <-- is the same as this.
Apakah ada alasan mengapa Anda harus menggunakan Function.identity()
daripada str->str
(atau sebaliknya). Saya pikir pilihan kedua lebih mudah dibaca (soal selera tentu saja). Tetapi, adakah alasan "nyata" mengapa seseorang harus lebih disukai?
java
lambda
java-8
java-stream
Przemysław Głębocki
sumber
sumber
t -> t
hanya karena lebih ringkas.Jawaban:
Pada implementasi JRE saat ini,
Function.identity()
akan selalu mengembalikan instance yang sama sementara setiap kemunculanidentifier -> identifier
tidak hanya akan membuat instance sendiri tetapi bahkan memiliki kelas implementasi yang berbeda. Untuk detail lebih lanjut, lihat di sini .Alasannya adalah bahwa kompiler menghasilkan metode sintetis yang memegang tubuh sepele dari ekspresi lambda (dalam kasus
x->x
, setara denganreturn identifier;
) dan memberitahu runtime untuk membuat implementasi dari antarmuka fungsional yang memanggil metode ini. Jadi runtime hanya melihat metode target yang berbeda dan implementasi saat ini tidak menganalisis metode untuk mengetahui apakah metode tertentu setara.Jadi,
Function.identity()
alih-alih menggunakanx -> x
mungkin menghemat memori, tetapi itu seharusnya tidak mendorong keputusan Anda jika Anda benar-benar berpikir itux -> x
lebih mudah dibaca daripadaFunction.identity()
.Anda juga dapat mempertimbangkan bahwa ketika kompilasi dengan informasi debug diaktifkan, metode sintetik akan memiliki atribut debug baris yang menunjuk ke baris kode sumber yang menahan ekspresi lambda, oleh karena itu Anda memiliki peluang untuk menemukan sumber
Function
instance tertentu saat debugging . Sebaliknya, ketika menemukan instance yang dikembalikan olehFunction.identity()
selama debugging suatu operasi, Anda tidak akan tahu siapa yang memanggil metode itu dan meneruskan instance tersebut ke operasi.sumber
x -> x
bingkai. Apakah Anda menyarankan untuk mengatur breakpoint ke lambda ini? Biasanya tidak mudah untuk menempatkan breakpoint ke dalam ekspresi tunggal lambda (setidaknya di Eclipse) ...Function.identity()
informasi itu hilang. Kemudian, rantai panggilan dapat membantu dalam kasus-kasus sederhana tetapi pikirkan, misalnya evaluasi multi-utas di mana inisiator asli tidak berada dalam jejak tumpukan ...new
.new Foo(…)
jaminan untuk membuat instance baru dari tipe yang tepatFoo
, sedangkan,Foo.getInstance(…)
dapat mengembalikan instance yang ada (subtipe dari)Foo
...Dalam contoh Anda tidak ada perbedaan besar antara
str -> str
danFunction.identity()
karena secara internal itu sederhanat->t
.Tetapi terkadang kita tidak bisa menggunakan
Function.identity
karena kita tidak bisa menggunakan aFunction
. Lihatlah di sini:ini akan dikompilasi dengan baik
tetapi jika Anda mencoba untuk mengkompilasi
Anda akan mendapatkan kesalahan kompilasi sejak
mapToInt
mengharapkanToIntFunction
, yang tidak terkait denganFunction
. JugaToIntFunction
tidak punyaidentity()
metode.sumber
i -> i
denganFunction.identity()
akan menghasilkan kesalahan kompiler.mapToInt(Integer::intValue)
.mapToInt(i -> i)
penyederhanaanmapToInt( (Integer i) -> i.intValue())
. Gunakan versi mana pun yang menurut Anda lebih jelas, bagi sayamapToInt(i -> i)
lebih baik tunjukkan niat kode ini.i -> i
terlihat seperti fungsi identitas, yang tidak ada dalam kasus ini.i -> i
karena tujuan saya adalah untuk memetakan Integer ke int (yangmapToInt
menyarankan dengan sangat baik) untuk tidak secara eksplisit memanggilintValue()
metode. Bagaimana pemetaan ini akan dicapai tidak terlalu penting. Jadi mari kita setuju untuk tidak setuju tetapi terima kasih karena menunjukkan kemungkinan perbedaan kinerja, saya perlu melihat lebih dekat pada suatu hari nanti.Dari sumber JDK :
Jadi, tidak, asalkan secara sintaksis benar.
sumber
t->t
dalam kode sumber dapat membuat satu objek dan implementasiFunction.identity()
adalah satu kemunculan. Jadi semua situs panggilan yang memanggilidentity()
akan membagikan satu objek itu sementara semua situs secara eksplisit menggunakan ekspresi lambdat->t
akan membuat objek mereka sendiri. MetodeFunction.identity()
ini tidak istimewa dengan cara apa pun, setiap kali Anda membuat metode pabrik merangkum ekspresi lambda yang umum digunakan dan memanggil metode itu alih-alih mengulangi ekspresi lambda, Anda dapat menghemat memori, mengingat implementasi saat ini .t->t
objek baru setiap kali metode dipanggil dan mendaur ulang objek yang sama setiap kali metode dipanggil?invokedynamic
instruksi yang dihubungkan pada eksekusi pertama dengan mengeksekusi yang disebut metode bootstrap, yang dalam kasus ekspresi lambda terletak diLambdaMetafactory
. Implementasi ini memutuskan untuk mengembalikan pegangan ke konstruktor, metode pabrik, atau kode yang selalu mengembalikan objek yang sama. Mungkin juga memutuskan untuk mengembalikan tautan ke pegangan yang sudah ada (yang saat ini tidak terjadi).