【问题标题】:Why java Instance initializers? [duplicate]为什么使用 java 实例初始化器? [复制]
【发布时间】:2011-07-20 14:29:57
【问题描述】:

Java 中的“实例初始化器”是什么意思?
我们不能把那段代码放在构造函数的开头吗?

【问题讨论】:

  • 如果java去掉这个特性也没什么大不了的。
  • @irreputable,我很少需要它们,但是匿名类不能有构造函数,但它们可以有实例初始化器,所以我们需要在语言中。
  • @Kaj 字段初始化器可以完成这项工作
  • @irreputable,仅当您需要为字段分配值时,不需要调用方法。

标签: java


【解决方案1】:

我经常使用它们,通常用于在一个语句中创建和填充 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);
    }
};

【讨论】:

  • 对于将实例初始化器用于集合填充的人,请查看Guava Library 作为更优雅和强大的替代方案。 Maps 类提供了不错的 Map 实用程序,它们的 Immutable* 类对于此处描述的用例特别好,请参阅 ImmutableMap
  • @Bohemian 我很困惑你怎么能调用 put(); ??前面没有变量名?
  • @goldencalf 它是一个匿名类,它是一个(即时)子类*,所以 put() 是一个实例方法,它在 this 上被隐式调用 - 就像你可以从任何实例方法调用toString(),而无需编码this.toString()。在示例中,我添加了put() 的重载版本,它只接受一个参数并且只在类定义中可见。
  • 看起来像是一个只有程序员自己才能阅读的经典代码示例(在他写了一年之后,直到他忘记了他到底在想什么......);)跨度>
  • @jack 实际上我发现这些 更容易 阅读,因为它们将对象及其数据绑定到一个结构中。分离的静态初始化块与正在初始化的对象没有明显的关联,实际上可能导致代码编译但在运行时爆炸。无论如何,它就像大多数编程习惯一样;一旦你习惯了它们,它们就会成为第二天性。
【解决方案2】:

您确实可以将代码放在每个构造函数的开头。然而,这正是实例初始化器的重点:它的代码应用于所有构造函数,如果你有许多构造函数和一些对所有构造函数都通用的代码,这会很方便。

(如果您刚开始编程,您可能不知道可以为同一个类创建许多构造函数(只要它们采用不同的参数);这称为 构造函数重载。如果你只有一个构造函数,那么实例初始化器确实不是很有用(编辑:除非你以创造性的方式滥用它,如其他答案所示)。

【讨论】:

  • 此外,即使使用单个构造函数,一起声明和初始化也比这里声明和那里初始化更具可读性和风险。
【解决方案3】:

您可以在声明匿名类时使用实例初始化器,例如,在实施the Double Brace Initialization Idiom 时。

List<String> mylist = new ArrayList<String>(){{add("a"); add("b"); add("c");}};

在这里你可以初始化对象,即使你不能向构造函数添加任何东西(因为类是匿名的)。

【讨论】:

  • 不过,在这种特殊情况下,我建议改用java.util.Arrays.&lt;T&gt;asList(T... ts) :-)
【解决方案4】:

因为这里的所有代码示例都使用匿名类,所以我将这个(有点可怕的)类放在一起,它演示了在“正确”类中使用实例初始化程序。您可以使用它们进行复杂的处理或在初始化时处理异常。请注意,这些块在构造函数运行之前运行,但是构造函数在子类中的初始化程序运行之前运行:

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

注意“新值”字符串直到父类的构造函数被调用后才被设置。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-17
    • 2016-05-30
    • 2013-04-26
    • 1970-01-01
    • 2013-02-21
    • 1970-01-01
    相关资源
    最近更新 更多