{{ ... }}
Java中的Double Brace初始化语法()是什么?
双括号初始化创建一个从指定类(外部大括号)派生的匿名类,并在该类(内部大括号)中提供初始化块.例如
new ArrayList() {{ add(1); add(2); }};
请注意,使用此双括号初始化的效果是您正在创建匿名内部类.创建的类具有this
指向周围外部类的隐式指针.虽然通常不是问题,但在某些情况下会引起悲痛,例如在序列化或垃圾收集时,值得注意这一点.
每当有人使用双支撑初始化时,一只小猫就会被杀死.
除了语法相当不寻常而且不是真正的惯用语(当然,品味是值得商榷的)之外,您在应用程序中不必要地创建了两个重要问题,我最近在这里更详细地讨论了这些问题.
每次使用双括号初始化时,都会生成一个新类.例如这个例子:
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有类似于现有数组文字的地图文字:
String[] array = { "John", "Doe" }; Map map = new HashMap() {{ put("John", "Doe"); }};
有些人可能会发现这种语法刺激.
第一个大括号创建一个新的匿名内部类.
第二组大括号创建一个实例初始值设定项,如Class中的静态块.
例如:
public class TestHashMap { public static void main(String[] args) { HashMapmap = 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概念,那么您可以轻松地将实例初始化程序块与静态初始化程序相关联,因为类似于大括号结构.唯一的区别是静态初始化程序添加了静态关键字,并且只运行一次; 无论你创建多少个对象.
更多
有关双支撑初始化的有趣应用,请参阅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
...和厚厚的培根做好准备!
我认为重要的是要强调Java中没有"Double Brace初始化"这样的东西.Oracle网站没有这个术语.在此示例中,有两个一起使用的功能:匿名类和初始化块.似乎旧的初始化程序块已被开发人员遗忘,并在本主题中引起一些混淆.来自Oracle文档的引文:
实例变量的初始化程序块看起来就像静态初始化程序块,但没有static关键字:
{ // whatever code is needed for initialization goes here }
避免双括号初始化的所有负面影响,例如:
破坏"等于"兼容性.
使用直接分配时不执行任何检查.
可能的内存泄漏.
做下一件事:
单独制作"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
1-没有双括号这样的东西:
我想指出没有双括号初始化这样的东西.只有正常的传统的一个支撑初始化块.第二个大括号块与初始化无关.答案说这两个括号初始化了一些东西,但它不是那样的.
2-这不仅仅是关于匿名类,而是所有类:
几乎所有答案都说它是创建匿名内部类时使用的东西.我认为阅读这些答案的人会觉得这只是在创建匿名内部类时才使用.但它用于所有类.阅读这些答案看起来是一些专门用于匿名课程的全新特殊功能,我认为这是误导性的.
3-目的只是将支架放在彼此之后,而不是新概念:
更进一步,这个问题讨论第二个开口支架刚好在第一个开启支架之后的情况.在普通类中使用时,通常在两个大括号之间有一些代码,但它完全相同.所以这是一个放置括号的问题.所以我认为我们不应该说这是一些新的令人兴奋的事情,因为这是我们都知道的事情,但只是在括号之间写了一些代码.我们不应该创建称为"双支撑初始化"的新概念.
4-创建嵌套的匿名类与两个大括号无关:
我不同意你创建了太多匿名类的参数.您不是因为初始化块而创建它们,而是因为您创建它们.即使您没有使用两个大括号初始化也会创建它们,因此即使没有初始化也会发生这些问题...初始化不是创建初始化对象的因素.
另外,我们不应该讨论通过使用这种不存在的东西"双支撑初始化"或者甚至通过正常的一个括号初始化来创建的问题,因为所描述的问题仅仅是因为创建匿名类而存在,因此它与原始问题无关.但是所有答案都给读者留下了印象,即它不是创建匿名类的错,而是这种邪恶的(不存在的)称为"双括号初始化"的东西.