{{ ... }}
Java'da Double Brace başlatma sözdizimi ( ) nedir?
{{ ... }}
Java'da Double Brace başlatma sözdizimi ( ) nedir?
Yanıtlar:
Çift ayraç başlatma, belirtilen sınıftan türetilen anonim bir sınıf oluşturur ( dış ayraçlar) ve bu sınıf içinde bir başlangıç bloğu ( iç ayraçlar) sağlar. Örneğin
new ArrayList<Integer>() {{
add(1);
add(2);
}};
Bu çift ayraç başlatmayı kullanmanın bir etkisinin, anonim iç sınıflar oluşturduğunuza dikkat edin. Oluşturulan sınıf, this
çevreleyen dış sınıfa örtülü bir işaretçi içerir. Normalde bir sorun olmasa da, örneğin serileştirirken veya çöp toplarken bazı durumlarda keder yaratabilir ve bunun farkında olmaya değer.
Birisi çift parantez başlatmayı her kullandığında, bir yavru kedi öldürülür.
Dışında sözdiziminden (tat tabii ki tartışılır) oldukça sıradışı ve gerçekten deyimsel olmama, gereksiz yere uygulamanızda iki önemli sorunlara, yaratıyor Sadece son zamanlarda burada daha ayrıntılı olarak blogged ettik .
Çift ayraç başlatmayı her kullandığınızda yeni bir sınıf oluşturulur. Örneğin bu örnek:
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");
}});
}});
}};
... şu sınıfları üretecek:
Test$1$1$1.class
Test$1$1$2.class
Test$1$1.class
Test$1.class
Test.class
Sınıf yükleyiciniz için oldukça fazla yük - hiçbir şey için! Tabii ki bir kez yaparsanız çok fazla başlatma süresi almayacaktır. Ama bunu kurumsal uygulamanız boyunca 20.000 kez yaparsanız ... tüm bu yığın bellek sadece "sözdizimi şekeri" için?
Yukarıdaki kodu alıp bu haritayı bir yöntemden döndürürseniz, o yöntemin arayanları şüphesiz çöp toplanamayan çok ağır kaynaklara tutunuyor olabilir. Aşağıdaki örneği düşünün:
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;
}
}
Döndürülen Map
artık, ekini içeren örneğine bir başvuru içerecektir ReallyHeavyObject
. Muhtemelen bunu riske atmak istemezsiniz:
Http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/ adresinden resim
Asıl sorunuzu cevaplamak için, insanlar Java'nın mevcut dizi değişmezlerine benzer bir harita değişmez değerleri gibi olduğunu iddia etmek için bu sözdizimini kullanıyorlar:
String[] array = { "John", "Doe" };
Map map = new HashMap() {{ put("John", "Doe"); }};
Bazı insanlar bunu sözdizimsel olarak uyarıcı bulabilir.
{{...}}
ve static
alan olarak bildirilirse , olası bir bellek sızıntısı olmamalı, yalnızca bir anonim sınıf ve ekli örnek başvurusu olmamalıdır, değil mi?
Map.of()
bu amaç için var, bu daha iyi bir çözüm olacak
ReallyHeavyObject
. Ayrıca, anonim iç sınıflar sınıf gövdesinde kullanılan tüm yerel değişkenleri yakalar, bu nedenle yalnızca bu desenle koleksiyonları veya haritaları başlatmak için sabitleri kullanmazsanız, iç sınıf örnekleri tümünü yakalar ve gerçekte kaldırıldığında bile bunlara başvurmaya devam eder toplama veya harita. Bu durumda, bu örneklerin referanslar için sadece iki kat daha fazla belleğe ihtiyacı yoktur, ancak bu konuda başka bir bellek sızıntısı vardır.
Örneğin:
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));
}
}
}
Nasıl çalışır
İlk küme ayracı yeni bir Anonim İç Sınıf oluşturur. Bu iç sınıflar, ebeveyn sınıflarının davranışlarına erişebilir. Yani, bizim durumumuzda, aslında HashSet sınıfının bir alt sınıfını yaratıyoruz, bu nedenle bu iç sınıf put () yöntemini kullanabiliyor.
Ve parantez İkinci seti başka bir şey örnek ilklendiriciler vardır. Çekirdek java kavramlarını hatırlatırsanız, yapıya benzer benzer küme nedeniyle örnek başlatıcı bloklarını statik başlatıcılarla kolayca ilişkilendirebilirsiniz. Tek fark, statik başlatıcısının statik anahtar kelimeyle eklenmesi ve yalnızca bir kez çalıştırılmasıdır; kaç nesne oluşturursanız olun.
Çift ayracı başlatmanın eğlenceli bir uygulaması için buraya Dwemthy'nin Java Dizisi'ne bakın .
Bir alıntı
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;
}}
Ve şimdi, BattleOfGrottoOfSausageSmells
ve ... tıknaz domuz pastırması için hazır olun !
Java'da "Double Brace başlatma" diye bir şeyin olmadığını vurgulamak önemlidir . Oracle web sitesinde bu terim yoktur. Bu örnekte birlikte kullanılan iki özellik vardır: anonim sınıf ve başlatıcı bloğu. Eski başlatıcı bloğu geliştiriciler tarafından unutulmuş gibi görünüyor ve bu konuda biraz karışıklığa neden oluyor. Dan Citation Oracle docs :
Örneğin değişkenler için başlatıcı blokları, statik başlatıcı blokları gibi görünür, ancak statik anahtar kelime olmadan:
{
// whatever code is needed for initialization goes here
}
1- Çifte parantez diye bir şey yoktur:
Çifte parantez başlatması diye bir şey olmadığını belirtmek isterim. Sadece normal bir geleneksel ayraç başlatma bloğu vardır. İkinci parantez bloğunun başlatma ile ilgisi yoktur. Cevaplar, bu iki parantezin bir şey başlattığını söylüyor, ancak böyle değil.
2- Bu sadece anonim sınıflarla ilgili değil, tüm sınıflarla ilgilidir:
Hemen hemen tüm cevaplar, anonim iç sınıflar oluşturulurken kullanılan bir şey olduğunu söyler. Bu cevapları okuyan insanların bunun sadece anonim iç sınıflar oluştururken kullanıldığı izlenimini edineceğini düşünüyorum. Ancak tüm sınıflarda kullanılır. Anlaşılan sınıflara adanmış yepyeni bir özel özellik gibi görünüyor bu cevapları okumak ve bu yanıltıcı olduğunu düşünüyorum.
3- Amaç, parantezleri birbiri ardına yerleştirmekle ilgilidir, yeni bir kavram değildir:
Daha ileriye dönük olarak , bu soru ikinci açma braketinin ilk açılış braketinden hemen sonra olduğu durumdan bahseder. Normal sınıfta kullanıldığında genellikle iki parantez arasında bazı kodlar vardır, ancak tamamen aynı şeydir. Bu yüzden parantez yerleştirme meselesi. Bu yüzden bunun yeni bir heyecan verici şey olduğunu söylememeliyiz, çünkü bu hepimizin bildiği şey, ancak parantezler arasında bazı kodlarla yazılmış. "İkili ayraç başlatma" adı verilen yeni bir konsept oluşturmamalıyız.
4- İç içe geçmiş anonim sınıflar oluşturmanın iki parantezle
ilgisi yoktur : Çok fazla anonim sınıf oluşturduğunuz argümanı kabul etmiyorum. Onları bir başlatma bloğu nedeniyle değil, sadece onları oluşturduğunuz için oluşturuyorsunuz. İki parantez başlatması kullanmasanız bile oluşturulacaklardı, böylece bu sorunlar başlatma olmadan bile ortaya çıkacaktı ... Başlatma, başlatılan nesneleri oluşturan faktör değildir.
Buna ek olarak, var olmayan bu şey "çift ayraç başlatma" veya hatta normal bir parantez başlatma ile oluşturulan sorun hakkında konuşmamalıyız, çünkü tarif edilen problemler sadece anonim sınıf oluşturma nedeniyle var olduğundan orijinal soru ile ilgisi yoktur. Ancak tüm cevaplar, okuyuculara anonim sınıflar yaratmanın hatası olmadığı, ancak "çift ayraç başlatma" adı verilen bu kötü (var olmayan) şey olduğu izlenimini verir.
Çift ayraç başlatmanın tüm olumsuz etkilerinden kaçınmak için, örneğin:
sonraki şeyleri yap:
Misal:
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 ; }
}
Kullanımı:
MyClass my = new MyClass.Builder(){{ first = 1; third = "3"; }}.create();
Avantajları:
Dezavantajları:
Ve sonuç olarak, şimdiye kadarki en basit java oluşturucu desenine sahibiz.
Github sitesindeki tüm örnekleri görün: java-sf-builder-simple-example
Diğer kullanımların yanı sıra koleksiyonları başlatmak için bir kısayoldur. Daha fazla bilgi edin ...
Koleksiyonu başlatmak için bazı Java ifadelerini döngü olarak koyabilirsiniz:
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));
}
};
@Lukas Eder tarafından işaret edildiği gibi, çift parantez koleksiyonlarının başlatılmasından kaçınılmalıdır.
Anonim bir iç sınıf oluşturur ve tüm iç sınıflar üst örneğe başvuruda bulunduğundan ve bu toplama nesnelerine yalnızca bildirilen nesneden daha fazla nesne tarafından başvurulursa, çöp toplamayı engelleyebilir - ve% 99 olasılıkla.
Java 9 kolaylık yöntemleri tanıttı List.of
, Set.of
ve Map.of
bunun yerine kullanılmalıdır. Çift ayraçlı başlatıcıdan daha hızlı ve daha verimlidirler.
İlk küme ayracı, yeni bir Anonim Sınıf oluşturur ve ikinci küme ayracı, statik blok gibi bir örnek başlatıcıları oluşturur.
Diğerlerinin işaret ettiği gibi, kullanımı güvenli değildir.
Ancak, koleksiyonları başlatmak için her zaman bu alternatifi kullanabilirsiniz.
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
List<String> list = List.of("A", "B", "C");
Bu, flash ve vbscript'te çok popüler olan with anahtar kelimesi ile aynı gibi görünecektir. Ne this
olduğunu ve daha fazlasını değiştirmenin bir yöntemi .
this
. Sözdizimi yalnızca anonim bir sınıf oluşturur (bu nedenle herhangi bir başvuru this
söz konusu yeni anonim sınıfın nesnesine atıfta bulunur) ve sonra {...}
yeni oluşturulan örneği başlatmak için bir başlatıcı bloğu kullanır .