Java技巧之双括弧初始化——匿名内部类的应用

Java技巧之双括弧初始化

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等)没有提供任何简便的语法结构,这使得在建立常量集合时的工作非常繁索。每次建立时我们都要做:

  1. 定义一个临时的集合类变量
  2. 建立一个空集合的实例,然后赋值给变量
  3. 将数据放入集合中
  4. 最后将集合做为参数传递给方法

例如,要将一个Set变量传给一个方法:

Java代码   收藏代码
  1. Set<String> validCodes = new HashSet<String>();  
  2. validCodes.add("XZ13s");  
  3. validCodes.add("AB21/X");  
  4. validCodes.add("YYLEX");  
  5. validCodes.add("AR2D");  
  6. removeProductsWithCodeIn(validCodes);  

也可以用静态初始的方法

Java代码   收藏代码
  1. private static final Set<String> validCodes = new HashSet<String>();  
  2. static {  
  3.        validCodes.add("XZ13s");  
  4.        validCodes.add("AB21/X");  
  5.        validCodes.add("YYLEX");  
  6.        validCodes.add("AR2D");  
  7. }  

其实,还有简结的方法,我们可以用双括弧语法(double-brace syntax)建立并初始化一个新的集合:

Java代码   收藏代码
  1. private static final Set<String> VALID_CODES = new HashSet<String>() {{  
  2.        add("XZ13s");  
  3.        add("AB21/X");  
  4.        add("YYLEX");  
  5.        add("AR2D");  
  6. }};  

或者

Java代码   收藏代码
  1. removeProductsWithCodeIn(new HashSet<String>() {{  
  2.        add("XZ13s");  
  3.        add("AB21/X");  
  4.        add("YYLEX");  
  5.        add("AR5E");  
  6. }});  

第一层括弧 实际是定义了一个内部匿名类 (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对象:

Java代码   收藏代码
  1. add(new JPanel() {{  
  2.        setLayout(...);  
  3.        setBorder(...);  
  4.        add(new JLabel(...));  
  5.        add(new JSpinner(...));  
  6. }});  

 这样建立的内部匿名类的实例中包函它容器对像的引用。如果串行化(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

  1. Declare a variable for a temporary collection
  2. Create a new empty collection and store a reference to it in the variable
  3. Put things into the collection
  4. Pass the collection to the method

E.g. To pass a set to a 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.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值