http://www.iteye.com/topic/418542
发表时间:2009-07-01
原文地址:http://www.c2.com/cgi/wiki?DoubleBraceInitialization 【20161129,试过可以访问英文原版,后附英文原版】
原作者:不详
译者:Alan Gao @ cgaolei.iteye.com
译者序:这是我在JavaIdioms(http://www.c2.com/cgi/wiki?JavaIdioms)上看到一个Java使用技巧。使用Java这么多年了,也还是头一次看到,还很实用。别看这小技巧好像很简单,但保证你不会在任何一书Java教材上看到,因为它不是一个真正的语法规范,而是一个语法的用法变种。小技巧还蕴含着Java的深层知识,就是身经百战的老手也不一定能说出其中奥妙。
翻译正文:
由于Java语言的集合框架中(collections, 如list, map, set等)没有提供任何简便的语法结构,这使得在建立常量集合时的工作非常繁索。每次建立时我们都要做:
- 定义一个临时的集合类变量
- 建立一个空集合的实例,然后赋值给变量
- 将数据放入集合中
- 最后将集合做为参数传递给方法
例如,要将一个Set变量传给一个方法:
- Set<String> validCodes = new HashSet<String>();
- validCodes.add("XZ13s");
- validCodes.add("AB21/X");
- validCodes.add("YYLEX");
- validCodes.add("AR2D");
- removeProductsWithCodeIn(validCodes);
也可以用静态初始的方法
- private static final Set<String> validCodes = new HashSet<String>();
- static {
- validCodes.add("XZ13s");
- validCodes.add("AB21/X");
- validCodes.add("YYLEX");
- validCodes.add("AR2D");
- }
其实,还有简结的方法,我们可以用双括弧语法(double-brace syntax)建立并初始化一个新的集合:
- private static final Set<String> VALID_CODES = new HashSet<String>() {{
- add("XZ13s");
- add("AB21/X");
- add("YYLEX");
- add("AR2D");
- }};
或者
- removeProductsWithCodeIn(new HashSet<String>() {{
- add("XZ13s");
- add("AB21/X");
- add("YYLEX");
- add("AR5E");
- }});
第一层括弧 实际是定义了一个内部匿名类 (Anonymous Inner Class),第二层括弧 实际上是一个实例初始化块 (instance initializer block),这个块在内部匿名类构造时被执行。这个块之所以被叫做“实例初始化块”是因为它们被定义在了一个类的实例范围内。这和“静态初始化块 (static initialzer)”不同,因为这种块在定义时在括弧前使用了static关键字,因此它的和类在同一个范围内的,也就是说当类加载时就会被执行(更详情,可参考Java语言规范http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.6 )。实例初始化块中可以使用其容器范围内的所有方法及变量,但特别需要注意的是实例初始化块是在构造器之前运行的。
这种方法只适用于不是final的类,因为final类是无法建立内部匿名子类,好在集合类都没有这个限制。因此,这种方法还可以被用来初始化其它任何对象,比如一个GUI对象:
- add(new JPanel() {{
- setLayout(...);
- setBorder(...);
- add(new JLabel(...));
- add(new JSpinner(...));
- }});
这样建立的内部匿名类的实例中包函它容器对像的引用。如果串行化(serialization)这个集合同时也会串行化它的外部类。
以下为上文提到的英文原版
http://wiki.c2.com/?DoubleBraceInitialization
Double Brace Initialization
One of the JavaIdioms . JavaLanguage doesn't have a convenient literal syntax for collections (lists, maps, sets, etc.). This makes creating constant collections or passing collections to functions quite laborious. Every time you have to- Declare a variable for a temporary collection
- Create a new empty collection and store a reference to it in the variable
- Put things into the collection
- Pass the collection to the method
Set<String> validCodes = new HashSet<String>(); validCodes.add("XZ13s"); validCodes.add("AB21/X"); validCodes.add("YYLEX"); validCodes.add("AR2D"); removeProductsWithCodeIn(validCodes);Or to initialize a set of constants:
private static final Set<String> VALID_CODES = new HashSet<String>(); static { validCodes.add("XZ13s"); validCodes.add("AB21/X"); validCodes.add("YYLEX"); validCodes.add("AR2D"); }But... you can create and initialize a new collection as an expression by using the "double-brace" syntax: E.g.
private static final Set<String> VALID_CODES = new HashSet<String>() {{ add("XZ13s"); add("AB21/X"); add("YYLEX"); add("AR2D"); }};Or:
removeProductsWithCodeIn(new HashSet<String>() {{ add("XZ13s"); add("AB21/X"); add("YYLEX"); add("AR5E"); }});The first brace creates a new AnonymousInnerClass , the second declares an instance initializer block that is run when the anonymous inner class is instantiated. This type of initializer block is formally called an "instance initializer", because it is declared within the instance scope of the class -- "static initializers" are a related concept where the keyword static is placed before the brace that starts the block, and which is executed at the class level as soon as the classloader completes loading the class (specified at http://docs.oracle.com/javase/specs/jls/se5.0/html/classes.html#8.6 ) The initializer block can use any methods, fields and final variables available in the containing scope, but one has to be wary of the fact that initializers are run before constructors (but not before superclass constructors). This only works for non-final classes because it creates an anonymous subclass. Obviously, this is not limited to collections; it can be used to initialize any kind of object -- for example Gui objects:
add(new JPanel() {{ setLayout(...); setBorder(...); add(new JLabel(...)); add(new JSpinner(...)); }});The instance of the anonymous class that you have created contain a synthetic reference to the enclosing object. If you serialise the collection you will also serialise everything in the outer class.
This idea is incompatible with one popular way to implement the equals(Object o) method. Assume class Example has this method:
public boolean equals(final Object o) { if (o == null) { return false; } else if (!getClass().equals(o.getClass())) { return false; } else { Example other = (Example) o; // Compare this to other. } }Then, objects created by DoubleBraceInitialization will never be equal to objects created without it. I would never use this for any class that needs a nontrivial equals(Object) method. Collection classes should be fine. So should the JPanel instance above. -- EricJablow It would be nice if Java only created and initialised an instance, not create a new class, for double brace initialisation and any anonymous "class" that does not add fields or override methods.
An idiomatic alternative, that doesn't require such heavy handed use of anonymous inner classes, is to use the constructor arguments to collections that accept another collection as the source data.
List<String> myList = new ArrayList<String>(Arrays.asList("One", "Two", "Three"));Although, this only works with collections that can be represented as a single array -- it doesn't work for maps. Or, use a VarargsCollectionFactoryMethod . >>Although this only works for collections<<< True but you can use Commons ArrayUtils.toMap(new MapUtils.toMap(new String[][] {{
{"RED", "#FF0000"}, {"GREEN", "#00FF00"}, {"BLUE", "#0000FF"}});
I call GreencoddsTenthRuleOfProgramming for having/trying to use a programming language for config info data-entry. A little bit in code is understandable. A lot is wrong-tool-smell. -t
Last edit June 15, 2014, See github about remodling.