Apa itu sintaks inisialisasi Double Brace ( {{ ... }}
) di Java?
Apa itu sintaks inisialisasi Double Brace ( {{ ... }}
) di Java?
Jawaban:
Inisialisasi penguat ganda menciptakan kelas anonim yang berasal dari kelas yang ditentukan ( kurung luar ), dan menyediakan blok penginisialisasi dalam kelas tersebut ( kurung dalam ). misalnya
new ArrayList<Integer>() {{
add(1);
add(2);
}};
Perhatikan bahwa efek menggunakan inisialisasi penjepit ganda ini adalah Anda membuat kelas dalam anonim. Kelas yang dibuat memiliki this
pointer implisit ke kelas luar sekitarnya. Meskipun biasanya tidak menjadi masalah, itu dapat menyebabkan kesedihan dalam beberapa keadaan misalnya ketika membuat cerita bersambung atau mengumpulkan sampah, dan ada baiknya menyadari hal ini.
Setiap kali seseorang menggunakan inisialisasi penjepit ganda, anak kucing terbunuh.
Terlepas dari sintaks yang agak tidak biasa dan tidak benar-benar idiomatis (rasa bisa diperdebatkan, tentu saja), Anda tidak perlu menciptakan dua masalah signifikan dalam aplikasi Anda, yang baru-baru ini saya blogging tentang lebih detail di sini .
Setiap kali Anda menggunakan inisialisasi brace ganda, kelas baru dibuat. Misalnya contoh ini:
Map source = new HashMap(){{
put("firstName", "John");
put("lastName", "Smith");
put("organizations", new HashMap(){{
put("0", new HashMap(){{
put("id", "1234");
}});
put("abc", new HashMap(){{
put("id", "5678");
}});
}});
}};
... akan menghasilkan kelas-kelas ini:
Test$1$1$1.class
Test$1$1$2.class
Test$1$1.class
Test$1.class
Test.class
Itu sedikit overhead untuk classloader Anda - gratis! Tentu saja tidak akan memakan banyak waktu inisialisasi jika Anda melakukannya sekali. Tetapi jika Anda melakukan ini 20'000 kali di seluruh aplikasi perusahaan Anda ... semua itu menumpuk memori hanya untuk sedikit "sintaks gula"?
Jika Anda mengambil kode di atas dan mengembalikan peta itu dari suatu metode, penelepon dari metode itu mungkin secara tidak curiga berpegangan pada sumber daya yang sangat berat yang tidak dapat dikumpulkan dari sampah. Perhatikan contoh berikut:
public class ReallyHeavyObject {
// Just to illustrate...
private int[] tonsOfValues;
private Resource[] tonsOfResources;
// This method almost does nothing
public Map quickHarmlessMethod() {
Map source = new HashMap(){{
put("firstName", "John");
put("lastName", "Smith");
put("organizations", new HashMap(){{
put("0", new HashMap(){{
put("id", "1234");
}});
put("abc", new HashMap(){{
put("id", "5678");
}});
}});
}};
return source;
}
}
Yang dikembalikan Map
sekarang akan berisi referensi ke instance terlampir ReallyHeavyObject
. Anda mungkin tidak ingin mengambil risiko itu:
Gambar dari http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/
Untuk menjawab pertanyaan Anda yang sebenarnya, orang-orang telah menggunakan sintaks ini untuk berpura-pura bahwa Java memiliki sesuatu seperti peta literal, mirip dengan liter array yang ada:
String[] array = { "John", "Doe" };
Map map = new HashMap() {{ put("John", "Doe"); }};
Beberapa orang mungkin merasa ini merangsang secara sintaksis.
{{...}}
dan dinyatakan sebagai static
bidang, seharusnya tidak ada kebocoran memori yang mungkin, hanya satu kelas anonim dan tidak ada referensi instance terlampir, bukan?
Map.of()
untuk tujuan itu, jadi itu akan menjadi solusi yang lebih baik
ReallyHeavyObject
. Juga, kelas dalam anonim menangkap semua variabel lokal yang digunakan dalam tubuh kelas, jadi jika Anda menggunakan tidak hanya konstanta untuk menginisialisasi koleksi atau peta dengan pola ini, instance kelas dalam akan menangkap semuanya dan terus referensi mereka bahkan ketika sebenarnya dihapus dari koleksi atau peta. Jadi jika demikian, contoh-contoh ini tidak hanya membutuhkan dua kali memori yang diperlukan untuk referensi, tetapi memiliki kebocoran memori lain dalam hal itu.
Sebagai contoh:
public class TestHashMap {
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<String,String>(){
{
put("1", "ONE");
}{
put("2", "TWO");
}{
put("3", "THREE");
}
};
Set<String> keySet = map.keySet();
for (String string : keySet) {
System.out.println(string+" ->"+map.get(string));
}
}
}
Bagaimana itu bekerja
Penjepit pertama menciptakan Kelas Batin Anonim baru. Kelas-kelas dalam ini mampu mengakses perilaku kelas induknya. Jadi, dalam kasus kami, kami sebenarnya membuat subkelas dari kelas HashSet, sehingga kelas dalam ini mampu menggunakan metode put ().
Dan set kedua kawat gigi tidak lain adalah inisialisasi instance. Jika Anda mengingatkan konsep inti Java maka Anda dapat dengan mudah mengaitkan blok initializer contoh dengan initializers statis karena kurung mirip seperti struct. Satu-satunya perbedaan adalah bahwa penginisialisasi statis ditambahkan dengan kata kunci statis, dan dijalankan hanya sekali; tidak peduli berapa banyak objek yang Anda buat.
Untuk aplikasi menyenangkan inisialisasi penjepit ganda, lihat di sini Array Dwemthy di Jawa .
Kutipan
private static class IndustrialRaverMonkey
extends Creature.Base {{
life = 46;
strength = 35;
charisma = 91;
weapon = 2;
}}
private static class DwarvenAngel
extends Creature.Base {{
life = 540;
strength = 6;
charisma = 144;
weapon = 50;
}}
Dan sekarang, bersiaplah untuk BattleOfGrottoOfSausageSmells
dan ... daging tebal!
Saya pikir penting untuk menekankan bahwa tidak ada yang namanya "inisialisasi Brace Ganda" di Jawa . Situs web Oracle tidak memiliki istilah ini. Dalam contoh ini ada dua fitur yang digunakan bersama: kelas anonim dan blok penginisialisasi. Sepertinya blok penginisialisasi lama telah dilupakan oleh pengembang dan menyebabkan beberapa kebingungan dalam topik ini. Kutipan dari Oracle docs :
Blok inisialisasi untuk variabel contoh terlihat seperti blok inisialisasi statis, tetapi tanpa kata kunci statis:
{
// whatever code is needed for initialization goes here
}
1- Tidak ada yang namanya kawat gigi ganda:
Saya ingin menunjukkan bahwa tidak ada yang namanya inisialisasi kawat gigi ganda . Hanya ada blok inisialisasi tradisional satu penyangga normal. Blok kawat gigi kedua tidak ada hubungannya dengan inisialisasi. Jawaban mengatakan bahwa kedua kawat gigi menginisialisasi sesuatu, tetapi tidak seperti itu.
2- Ini bukan hanya tentang kelas anonim tetapi semua kelas:
Hampir semua jawaban berbicara bahwa itu adalah hal yang digunakan saat membuat kelas batin anonim. Saya pikir orang-orang yang membaca jawaban-jawaban itu akan mendapatkan kesan bahwa ini hanya digunakan ketika membuat kelas batin anonim. Tetapi digunakan di semua kelas. Membaca jawaban-jawaban itu sepertinya adalah beberapa fitur khusus baru yang didedikasikan untuk kelas anonim dan saya pikir itu menyesatkan.
3 - Tujuannya adalah tentang menempatkan braket satu sama lain, bukan konsep baru:
Lebih jauh, pertanyaan ini berbicara tentang situasi ketika braket pembuka kedua tepat setelah braket pembuka pertama. Ketika digunakan di kelas normal biasanya ada beberapa kode antara dua kawat gigi, tetapi sama sekali hal yang sama. Jadi itu masalah menempatkan kurung. Jadi saya pikir kita tidak boleh mengatakan bahwa ini adalah hal baru yang menarik, karena ini adalah hal yang kita semua tahu, tetapi hanya ditulis dengan beberapa kode di antara kurung. Kita seharusnya tidak membuat konsep baru yang disebut "inisialisasi penjepit ganda".
4- Membuat kelas anonim bersarang tidak ada hubungannya dengan dua kawat gigi:
Saya tidak setuju dengan argumen bahwa Anda membuat terlalu banyak kelas anonim. Anda tidak membuatnya karena blok inisialisasi, tetapi hanya karena Anda membuatnya. Mereka akan dibuat bahkan jika Anda tidak menggunakan inisialisasi dua kawat gigi sehingga masalah-masalah itu akan terjadi bahkan tanpa inisialisasi ... Inisialisasi bukanlah faktor yang menciptakan objek yang diinisialisasi.
Selain itu kita tidak boleh berbicara tentang masalah yang dibuat dengan menggunakan hal yang tidak ada ini "inisialisasi penjepit ganda" atau bahkan dengan inisialisasi satu braket normal, karena masalah yang dijelaskan ada hanya karena membuat kelas anonim sehingga tidak ada hubungannya dengan pertanyaan asli. Tetapi semua jawaban dengan memberi kesan kepada pembaca bahwa itu bukan kesalahan menciptakan kelas anonim, tetapi hal jahat (tidak ada) ini disebut "inisialisasi penjepit ganda".
Untuk menghindari semua efek negatif dari inisialisasi penjepit ganda, seperti:
lakukan hal-hal selanjutnya:
Contoh:
public class MyClass {
public static class Builder {
public int first = -1 ;
public double second = Double.NaN;
public String third = null ;
public MyClass create() {
return new MyClass(first, second, third);
}
}
protected final int first ;
protected final double second;
protected final String third ;
protected MyClass(
int first ,
double second,
String third
) {
this.first = first ;
this.second= second;
this.third = third ;
}
public int first () { return first ; }
public double second() { return second; }
public String third () { return third ; }
}
Pemakaian:
MyClass my = new MyClass.Builder(){{ first = 1; third = "3"; }}.create();
Keuntungan:
Kekurangan:
Dan, sebagai hasilnya, kami memiliki pola java builder paling sederhana.
Lihat semua sampel di github: java-sf-builder-simple-example
Ini - di antara kegunaan lain - jalan pintas untuk menginisialisasi koleksi. Belajarlah lagi ...
Anda bisa meletakkan beberapa pernyataan Java sebagai loop untuk menginisialisasi koleksi:
List<Character> characters = new ArrayList<Character>() {
{
for (char c = 'A'; c <= 'E'; c++) add(c);
}
};
Random rnd = new Random();
List<Integer> integers = new ArrayList<Integer>() {
{
while (size() < 10) add(rnd.nextInt(1_000_000));
}
};
Seperti yang ditunjukkan oleh @Lukas Eder inisialisasi kawat gigi ganda koleksi harus dihindari.
Itu menciptakan kelas batin anonim, dan karena semua kelas internal tetap referensi ke instance induk, ia dapat - dan 99% kemungkinan akan - mencegah pengumpulan sampah jika objek pengumpulan ini dirujuk oleh lebih banyak objek daripada hanya yang mendeklarasikan.
Java 9 telah memperkenalkan metode kenyamanan List.of
, Set.of
dan Map.of
, yang harus digunakan sebagai gantinya. Mereka lebih cepat dan lebih efisien daripada penginisialisasi double-brace.
Penjepit pertama membuat Kelas Anonim baru dan set penjepit kedua menciptakan inisialisasi instance seperti blok statis.
Seperti yang telah ditunjukkan orang lain, itu tidak aman untuk digunakan.
Namun, Anda selalu dapat menggunakan alternatif ini untuk menginisialisasi koleksi.
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
List<String> list = List.of("A", "B", "C");
Ini akan tampak sama dengan kata kunci dengan begitu populer di flash dan vbscript. Ini adalah metode untuk mengubah apa yang this
ada dan tidak lebih.
this
ada. Sintaks hanya membuat kelas anonim (jadi referensi apa pun this
akan merujuk ke objek dari kelas anonim baru), dan kemudian menggunakan blok {...}
penginisialisasi untuk menginisialisasi instance yang baru dibuat.