Почему инициализаторы Java-экземпляра?

В чем смысл "инициализаторов экземпляров" в Java?
Разве мы не можем просто поместить этот блок кода в начало конструктора?

4 ответа

Я использую их очень часто, обычно для создания и заполнения 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");
    }
});

Намного аккуратнее, чем использование статических блоков и работа с единичными заданиями на финал и т. Д.

И еще один совет: не бойтесь создавать методы, которые упрощают ваш экземпляр блока:

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);
    }
};

Вы действительно можете поместить код в начало каждого конструктора. Однако в этом и заключается смысл инициализатора экземпляра: его код применяется ко всем конструкторам, что может быть удобно, если у вас много конструкторов и немного кода, который является общим для всех них.

(Если вы только начинаете с программирования, вы, возможно, не знали, что можно создать много конструкторов для одного и того же класса (если они принимают разные параметры); это называется перегрузкой конструктора. Если у вас есть только один конструктор, тогда инициализатор экземпляра действительно не очень полезен (Edit: если вы не злоупотребляете им в творческой моде, как показано в других ответах).)

Поскольку все примеры кода здесь используют анонимные классы, я собрал этот (немного ужасающий) класс, который демонстрирует использование инициализаторов экземпляров в "правильном" классе. Вы можете использовать их для сложной обработки или обработки исключений во время инициализации. Обратите внимание, что эти блоки запускаются до запуска конструктора, но конструктор запускается до запуска инициализаторов в дочернем классе:

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

Обратите внимание, что строка "новое значение" не устанавливается до тех пор, пока конструктор родительского класса уже не был вызван.

Вы можете использовать инициализатор экземпляра при объявлении анонимного класса, например, при выполнении идиомы инициализации двойной скобки.

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

Здесь вы можете инициализировать объект, даже если вы ничего не можете добавить в конструктор (потому что класс анонимный).

Другие вопросы по тегам