Java'da Double Brace başlatma nedir?


307

{{ ... }}Java'da Double Brace başlatma sözdizimi ( ) nedir?




10
Double Brace başlatması çok tehlikeli bir özelliktir ve mantıklı bir şekilde kullanılmalıdır. Sözleşme eşittir ve zor bellek sızıntılarına neden olabilir. Bu makalede ayrıntılar açıklanmaktadır.
Andrii Polunin

Yanıtlar:


303

Ç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 ( 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.


11
İç ve dış parantezlerin anlamını açıkladığınız için teşekkürler. Aniden neden özel bir anlamla izin verilen iki parantez olduğunu merak ettim, aslında sadece bazı sihirli yeni numaralar olarak görünen normal java yapıları olduklarında. Bunun gibi şeyler Java sözdizimini sorgulamama neden oluyor. Zaten bir uzman değilseniz, okumak ve yazmak çok zor olabilir.
jackthehipster

4
Bunun gibi "sihirli sözdizimi" birçok dilde mevcuttur, örneğin neredeyse tüm C benzeri diller, garip bir şekilde "x--> 0" olan döngüler için "x -> 0" ın "0'a gider" sözdizimini destekler. boşluk yerleştirme.
Joachim Sauer

17
Sadece "çift ayraç başlatma" nın kendi başına mevcut olmadığı sonucuna varabiliriz, sadece anonim bir sınıf ve bir başlatıcı bloğu oluşturmanın bir kombinasyonudur , bir kez birleştirildiğinde sözdizimsel bir yapıya benziyor , ancak gerçekte t.
MC İmparatoru

Teşekkür ederim! Gson, anonim iç sınıf kullanımı nedeniyle çift ayraç başlatma ile serileştirdiğimizde null değerini döndürür.
Pradeep AJ- Msft MVP

295

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 .

1. Çok fazla anonim sınıf oluşturuyorsunuz

Ç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?

2. Potansiyel olarak bir bellek sızıntısı oluşturuyorsunuz!

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 Mapartık, ekini içeren örneğine bir başvuru içerecektir ReallyHeavyObject. Muhtemelen bunu riske atmak istemezsiniz:

Burada Bellek Sızıntısı

Http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/ adresinden resim

3. Java'nın harita değişmezleri olduğunu iddia edebilirsiniz

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.


11
"Sen yol çok anonim sınıfları oluştururken" - (diyelim) Scala anonim sınıfları nasıl oluşturduğunu bakarak, ben çok emin bu olduğunu değilim büyük sorun
Brian Agnew

2
Statik haritalar bildirmenin geçerli ve güzel bir yolu olarak kalmıyor mu? Bir HashMap başlatılır {{...}}ve staticalan 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?
lorenzo-s

8
@ lorenzo-s: Evet, 2) ve 3) geçerli değil, sadece 1). Neyse ki, Java 9 ile nihayet Map.of()bu amaç için var, bu daha iyi bir çözüm olacak
Lukas Eder

3
İç haritaların da dış haritalara ve dolaylı olarak dolaylı olarak referanslara sahip olduğunu belirtmek gerekir 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.
Holger

41
  • İlk küme ayracı yeni bir Anonim İç Sınıf oluşturur.
  • İkinci küme ayracı kümesi, Class'ta statik blok gibi bir örnek başlatıcıları oluşturur.

Ö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.

Daha


24

Ç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, BattleOfGrottoOfSausageSmellsve ... tıknaz domuz pastırması için hazır olun !


16

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
}

11

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.


9

Çift ayraç başlatmanın tüm olumsuz etkilerinden kaçınmak için, örneğin:

  1. Bozuk "eşit" uyumluluk.
  2. Doğrudan atamalar kullanıldığında kontrol yapılmaz.
  3. Olası bellek sızıntıları.

sonraki şeyleri yap:

  1. Özellikle çift ayraç başlatma için ayrı bir "Builder" sınıfı yapın.
  2. Alanları varsayılan değerlerle bildirin.
  3. Nesne oluşturma yöntemini bu sınıfa koyun.

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ı:

  1. Sadece kullanmak için.
  2. "Eşittir" uyumluluğunu kırmayın.
  3. Kontrolleri oluşturma yönteminde yapabilirsiniz.
  4. Bellek sızıntısı yok.

Dezavantajları:

  • Yok.

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



4

Bunun gibi bir şey mi ifade etmeye çalışıyorsun?

List<String> blah = new ArrayList<String>(){{add("asdfa");add("bbb");}};

oluşturma zamanında bir dizi listesi başlatma (hack)


4

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));
    }
};

Ancak bu durum performansı etkiler, bu tartışmayı kontrol edin


4

@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.ofve Map.ofbunun yerine kullanılmalıdır. Çift ayraçlı başlatıcıdan daha hızlı ve daha verimlidirler.


0

İ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.

  • Java 8
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
  • Java 9
List<String> list = List.of("A", "B", "C");

-1

Bu, flash ve vbscript'te çok popüler olan with anahtar kelimesi ile aynı gibi görünecektir. Ne thisolduğunu ve daha fazlasını değiştirmenin bir yöntemi .


Pek sayılmaz. Bu, yeni bir sınıf yaratmanın, neyin değiştiğini değiştirmek için bir yöntem olduğunu söylemek gibi bir şeydir this. Sözdizimi yalnızca anonim bir sınıf oluşturur (bu nedenle herhangi bir başvuru thissö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 .
grinch
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.