当前位置:  开发笔记 > 编程语言 > 正文

什么是Java中的Double Brace初始化?

如何解决《什么是Java中的DoubleBrace初始化?》经验,为你挑选了7个好方法。

{{ ... }}Java中的Double Brace初始化语法()是什么?



1> Brian Agnew..:

双括号初始化创建一个从指定类(外部大括号)派生的匿名类,并在该类(内部大括号)中提供初始化块.例如

new ArrayList() {{
   add(1);
   add(2);
}};

请注意,使用此双括号初始化的效果是您正在创建匿名内部类.创建的类具有this指向周围外部类的隐式指针.虽然通常不是问题,但在某些情况下会引起悲痛,例如在序列化或垃圾收集时,值得注意这一点.


我们可以得出结论,"双支撑初始化"本身并不存在,它只是创建一个*匿名类*和一个*初始化块*的组合,一旦组合,*看起来像一个语法结构,但是实际上,事实并非如此.
感谢您澄清内外支撑的含义.我想知道为什么突然有两个括号被允许具有特殊含义,当它们实际上是普通的java结构时,它们只是作为一些神奇的新技巧出现.这样的事情让我质疑Java语法.如果你不是专家,那么阅读和写作都会非常棘手.
像这样的"魔术语法"存在于许多语言中,例如,几乎所有类C语言都支持for循环中"x - > 0"的"转到0"语法,这只是"x - > 0",很奇怪空间布局.

2> Lukas Eder..:

每当有人使用双支撑初始化时,一只小猫就会被杀死.

除了语法相当不寻常而且不是真正的惯用语(当然,品味是值得商榷的)之外,您在应用程序中不必要地创建了两个重要问题,我最近在这里更详细地讨论了这些问题.

你创造了太多的匿名课程

每次使用双括号初始化时,都会生成一个新类.例如这个例子:

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

...将产生这些类:

Test$1$1$1.class
Test$1$1$2.class
Test$1$1.class
Test$1.class
Test.class

这对你的类加载器来说是一个相当大的开销 - 什么都不是!当然,如果你这样做一次,它将不需要太多的初始化时间.但是,如果你在整个企业应用程序中执行此操作20,000次...所有堆内存只是为了一点"语法糖"?

你可能会造成内存泄漏!

如果您使用上面的代码并从方法返回该映射,那么该方法的调用者可能会毫无疑问地保留非常繁重的资源,而这些资源无法进行垃圾回收.请考虑以下示例:

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

返回的Map现在将包含对封闭实例的引用ReallyHeavyObject.你可能不想冒这样的风险:

记忆泄漏就在这里

图片来自http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/

你可以假装Java有地图文字

为了回答你的实际问题,人们一直在使用这种语法假装Java有类似于现有数组文字的地图文字:

String[] array = { "John", "Doe" };
Map map = new HashMap() {{ put("John", "Doe"); }};

有些人可能会发现这种语法刺激.


"你正在创建太多匿名类" - 看看Scala如何创建匿名类,我不太确定这是一个*主要*问题
@ lorenzo-s:是的,2)和3)不适用于那时,只有1).幸运的是,使用Java 9,最终会出现`Map.of()`,这将是一个更好的解决方案
它不是一个有效和好的方式来声明静态地图吗?如果HashMap用`{{...}}`初始化并声明为`static`字段,那么不应该有任何可能的内存泄漏,只有一个匿名类而没有封闭的实例引用,对吧?
可能值得注意的是,内部映射也引用了外部映射,因此间接引用了“ ReallyHeavyObject”。另外,匿名内部类会捕获在类主体中使用的所有局部变量,因此,如果您不仅使用常量以这种模式初始化集合或映射,内部类实例将捕获所有它们并继续引用它们,即使实际上已从中删除它们也是如此。集合或地图。因此,在这种情况下,这些实例不仅需要为引用添加两倍的必要内存,而且在这方面还存在另一个内存泄漏。

3> Premraj..:

第一个大括号创建一个新的匿名内部类.

第二组大括号创建一个实例初始值设定项,如Class中的静态块.

例如:

   public class TestHashMap {
    public static void main(String[] args) {
        HashMap map = new HashMap(){
        {
            put("1", "ONE");
        }{
            put("2", "TWO");
        }{
            put("3", "THREE");
        }
        };
        Set keySet = map.keySet();
        for (String string : keySet) {
            System.out.println(string+" ->"+map.get(string));
        }
    }

}

这个怎么运作

第一个大括号创建一个新的匿名内部类.这些内部类能够访问其父类的行为.所以,在我们的例子中,我们实际上是在创建一个HashSet类的子类,所以这个内部类能够使用put()方法.

第二组括号都不过是实例初始化.如果您提醒核心Java概念,那么您可以轻松地将实例初始化程序块与静态初始化程序相关联,因为类似于大括号结构.唯一的区别是静态初始化程序添加了静态关键字,并且只运行一次; 无论你创建多少个对象.

更多



4> akuhn..:

有关双支撑初始化的有趣应用,请参阅Java中的Dwemthy数组.

摘录

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

现在,为BattleOfGrottoOfSausageSmells...和厚厚的培根做好准备!



5> Alex T..:

我认为重要的是要强调Java中没有"Double Brace初始化"这样的东西.Oracle网站没有这个术语.在此示例中,有两个一起使用的功能:匿名类和初始化块.似乎旧的初始化程序块已被开发人员遗忘,并在本主题中引起一些混淆.来自Oracle文档的引文:

实例变量的初始化程序块看起来就像静态初始化程序块,但没有static关键字:

{
    // whatever code is needed for initialization goes here
}



6> 小智..:

避免双括号初始化的所有负面影响,例如:

    破坏"等于"兼容性.

    使用直接分配时不执行任何检查.

    可能的内存泄漏.

做下一件事:

    单独制作"Builder"类,特别是双括号初始化.

    使用默认值声明字段.

    将对象创建方法放在该类中.

例:

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

用法:

MyClass my = new MyClass.Builder(){{ first = 1; third = "3"; }}.create();

好处:

    简单地使用.

    不要打破"等于"兼容性.

    您可以在创建方法中执行检查.

    没有内存泄漏.

缺点:

没有.

因此,我们拥有最简单的java构建器模式.

查看github上的所有示例:java-sf-builder-simple-example



7> ctomek..:

1-没有双括号这样的东西:
我想指出没有双括号初始化这样的东西.只有正常的传统的一个支撑初始化块.第二个大括号块与初始化无关.答案说这两个括号初始化了一些东西,但它不是那样的.

2-这不仅仅是关于匿名类,而是所有类:
几乎所有答案都说它是创建匿名内部类时使用的东西.我认为阅读这些答案的人会觉得这只是在创建匿名内部类时才使用.但它用于所有类.阅读这些答案看起来是一些专门用于匿名课程的全新特殊功能,我认为这是误导性的.

3-目的只是将支架放在彼此之后,而不是新概念:
更进一步,这个问题讨论第二个开口支架刚好在第一个开启支架之后的情况.在普通类中使用时,通常在两个大括号之间有一些代码,但它完全相同.所以这是一个放置括号的问题.所以我认为我们不应该说这是一些新的令人兴奋的事情,因为这是我们都知道的事情,但只是在括号之间写了一些代码.我们不应该创建称为"双支撑初始化"的新概念.

4-创建嵌套的匿名类与两个大括号无关:
我不同意你创建了太多匿名类的参数.您不是因为初始化块而创建它们,而是因为您创建它们.即使您没有使用两个大括号初始化也会创建它们,因此即使没有初始化也会发生这些问题...初始化不是创建初始化对象的因素.

另外,我们不应该讨论通过使用这种不存在的东西"双支撑初始化"或者甚至通过正常的一个括号初始化来创建的问题,因为所描述的问题仅仅是因为创建匿名类而存在,因此它与原始问题无关.但是所有答案都给读者留下了印象,即它不是创建匿名类的错,而是这种邪恶的(不存在的)称为"双括号初始化"的东西.

推荐阅读
殉情放开那只小兔子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有