Java中的“实例初始化器”有什么意义?
我们不能将那一块代码放在构造函数的开始吗?
Java中的“实例初始化器”有什么意义?
我们不能将那一块代码放在构造函数的开始吗?
我经常使用它们,通常用于在一条语句中创建和填充Map(而不是使用丑陋的静态块):
private static final Map<String, String> CODES = new HashMap<String, String>() {
{
put("A", "Alpha");
put("B", "Bravo");
}
};
给这个操作增加一些趣味性和实用性的方法之一是在一个语句中创建一个不可修改的映射:
private static final Map<String, String> CODES =
Collections.unmodifiableMap(new HashMap<String, String>() {
{
put("A", "Alpha");
put("B", "Bravo");
}
});
这种方法比使用静态块并处理对final的单一赋值要更加简洁。
还有一个提示:不要害怕创建能够简化实例块的方法:
private static final Map<String, String> CODES = new HashMap<String, String>() {
{
put("Alpha");
put("Bravo");
}
void put(String code) {
put(code.substring(0, 1), code);
}
};
Maps
类提供了一些很好用的Map工具,而它们的Immutable*
类则特别适合描述这里所说的用例,可以看看ImmutableMap
。 - dimo414put()
是一个实例方法,它会隐式地在this
上调用 - 就像您可以从任何实例方法中调用toString()
而不必编写this.toString()
一样。在示例中,我添加了一个重载版本的put()
,它只接受一个参数,并且仅在类定义内可见。 - Bohemian您确实可以在每个构造函数的开头放置代码。然而,这正是实例初始化器的作用:它的代码适用于所有构造函数,如果您有许多构造函数和一些公共的代码,则这非常方便。
(如果您刚开始学习编程,可能不知道可以为同一个类创建多个构造函数(只要它们采用不同的参数);这被称为构造函数重载。如果您只有一个构造函数,则实例初始化器确实没有什么用处(编辑:除非您以其他答案中所示的创造性方式滥用它)。)
当声明匿名类时,您可以使用实例初始化程序,例如,在执行双括号初始化习惯用法时。
List<String> mylist = new ArrayList<String>(){{add("a"); add("b"); add("c");}};
java.util.Arrays.<T>asList(T... ts)
来代替 :-) - Platinum Azure由于这里的所有代码示例都使用匿名类,因此我编写了一个(有点可怕的)类,演示如何在“正式”类中使用实例初始化器。您可以使用它们来进行复杂处理或在初始化时处理异常。请注意,这些块在构造函数运行之前运行,但是构造函数在子类中的初始化器运行之前运行:
import java.util.Scanner;
public class InstanceInitializer {
int x;
{
try {
System.out.print("Enter a number: ");
x = Integer.parseInt(new Scanner(System.in).nextLine());
} catch (NumberFormatException e) {
x = 0;
}
}
String y;
{
System.out.print("Enter a string: ");
y = new Scanner(System.in).nextLine();
for(int i = 0; i < 3; i++)
y += y;
}
public InstanceInitializer() {
System.out.println("The value of x is "+x);
System.out.println("The value of y is "+y);
}
public static class ChildInstanceInitializer extends InstanceInitializer {
{
y = "a new value set by the child AFTER construction";
}
}
public static void main(String[] args){
new InstanceInitializer();
new InstanceInitializer();
System.out.println();
System.out.println(new ChildInstanceInitializer().y);
// This is essentially the same as:
System.out.println(new InstanceInitializer(){
{y = "a new value set by the child AFTER construction";}
}.y);
}
}
Enter a number: 1
Enter a string: a
The value of x is 1
The value of y is aaaaaaaa
Enter a number: q
Enter a string: r
The value of x is 0
The value of y is rrrrrrrr
Enter a number: 3
Enter a string: b
The value of x is 3
The value of y is bbbbbbbb
a new value set by the child AFTER construction
Enter a number: s
Enter a string: Hello
The value of x is 0
The value of y is HelloHelloHelloHelloHelloHelloHelloHello
a new value set by the child AFTER construction