Почему новый Foo () {{}} вызывает автоматическое подклассу [дубликат]

Java всегда передает аргументы по значению NOT по ссылке.


Позвольте мне объяснить это с помощью примера :

public class Main{
     public static void main(String[] args){
          Foo f = new Foo("f");
          changeReference(f); // It won't change the reference!
          modifyReference(f); // It will modify the object that the reference variable "f" refers to!
     }
     public static void changeReference(Foo a){
          Foo b = new Foo("b");
          a = b;
     }
     public static void modifyReference(Foo c){
          c.setAttribute("c");
     }
}

объясните это поэтапно:

  1. Объявление ссылки с именем f типа Foo и присвоение ее новому объекту типа Foo с атрибутом "f".
    Foo f = new Foo("f");
    
    enter image description here [/g1]
  2. Со стороны метода указывается ссылка типа Foo с именем a и изначально назначена null.
    public static void changeReference(Foo a)
    
    enter image description here [/g2]
  3. Когда вы вызываете метод changeReference, ссылка a будет назначена объекту, который передается в качестве аргумента.
    changeReference(f);
    
    enter image description here [/g3]
  4. Объявление ссылки с именем b типа Foo и присвоение ее новому объекту типа Foo с атрибутом "b".
    Foo b = new Foo("b");
    
    enter image description here [/g4]
  5. a = b переназначает ссылку a NOT f объекту, чей его атрибут "b". enter image description here [/g5]
  6. Как вы называете метод modifyReference(Foo c), для объекта с атрибутом "f" создается и назначается ссылка c. enter image description here [/g6]
  7. c.setAttribute("c"); изменит атрибут объекта, на который указывает ссылка c, и это тот же объект, на который указывает ссылка f. enter image description here [/g7]

Надеюсь, теперь вы понимаете, как объекты передачи в качестве аргументов работают в Java:)

209
задан Andrew Tobilko 1 June 2016 в 10:48
поделиться

11 ответов

Инициализация двойной скобки создает анонимный класс, полученный из указанного класса (внешние фигурные скобки external ) и предоставляет блок инициализации внутри этого класса (скобки inner ). например,

new ArrayList<Integer>() {{
   add(1);
   add(2);
}};

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

207
ответ дан Brian Agnew 28 August 2018 в 06:15
поделиться

Для забавного применения инициализации двойной скобки см. здесь Массив Dwemthy's в Java .

Отрывок

private static class IndustrialRaverMonkey
  extends Creature.Base {{
    life = 46;
    strength = 35;
    charisma = 91;
    weapon = 2;
  }}

private static class DwarvenAngel
  extends Creature.Base {{
    life = 540;
    strength = 6;
    charisma = 144;
    weapon = 50;
  }}

И теперь, будьте готовы для BattleOfGrottoOfSausageSmells и & hellip; короткое бекон!

21
ответ дан akuhn 28 August 2018 в 06:15
поделиться

Я думаю, что важно подчеркнуть, что в Java нет такой вещи, как «Инициализация двойного браслеза». Веб-сайт Oracle не имеет этого термина. В этом примере используются две функции: анонимный класс и блок инициализатора. Похоже, что старый блок инициализатора был забыт разработчиками и вызвал некоторую путаницу в этой теме. Цитирование из Oracle docs :

Блоки инициализатора для переменных экземпляра выглядят так же, как и статические блоки инициализатора, но без статического ключевого слова:

{
    // whatever code is needed for initialization goes here
}
9
ответ дан Alex T 28 August 2018 в 06:15
поделиться

Чтобы избежать всех отрицательных эффектов инициализации двойной комбинации, например:

  1. Сломанная «равна» совместимости.
  2. При выполнении прямых назначений никаких проверок не выполнялось.
  3. Возможные утечки памяти.

делать следующие вещи:

  1. Сделать отдельный класс «Builder» особенно для инициализации двойной привязки.
  2. Объявить поля со значениями по умолчанию.
  3. Поместить метод создания объекта в этот класс.

Пример:

public class MyClass {
    public static class Builder {
        public int    first  = -1        ;
        public double second = Double.NaN;
        public String third  = null      ;

        public MyClass create() {
            return new MyClass(first, second, third);
        }
    }

    protected final int    first ;
    protected final double second;
    protected final String third ;

    protected MyClass(
        int    first ,
        double second,
        String third
    ) {
        this.first = first ;
        this.second= second;
        this.third = third ;
    }

    public int    first () { return first ; }
    public double second() { return second; }
    public String third () { return third ; }
}

Использование :

MyClass my = new MyClass.Builder(){{ first = 1; third = "3"; }}.create();

Преимущества:

  1. Просто использовать.
  2. Не разрывает совместимость «равно».
  3. Вы может выполнять проверки в методе создания.
  4. Отсутствует утечка памяти.

Недостатки:

  • Нет.

И, как результат, у нас самый простой шаблон java-строителя.

См. все образцы в github: java-sf-builder-simple-example

4
ответ дан Boris Podchezertsev 28 August 2018 в 06:15
поделиться

Это будет выглядеть так же, как и ключевое слово с таким популярным в flash и vbscript. Это метод изменения того, что this есть, и не более того.

0
ответ дан Chuck Vose 28 August 2018 в 06:15
поделиться

Вы можете поместить некоторые инструкции Java в цикл для инициализации коллекции:

List<Character> characters = new ArrayList<Character>() {
    {
        for (char c = 'A'; c <= 'E'; c++) add(c);
    }
};

Random rnd = new Random();

List<Integer> integers = new ArrayList<Integer>() {
    {
         while (size() < 10) add(rnd.nextInt(1_000_000));
    }
};

Но этот случай влияет на производительность, проверьте это обсуждение

3
ответ дан Community 28 August 2018 в 06:15
поделиться

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

Во-вторых, почти все ответы говорят о том, что это используется при создании анонимных внутренних классов. Я думаю, что люди, читающие эти ответы, получат впечатление, что это используется только при создании анонимных внутренних классов. Но он используется во всех классах. Чтение этих ответов - это какое-то новое особое будущее, посвященное анонимным классам, и я думаю, что это вводит в заблуждение.

. Идем дальше, этот вопрос говорит о ситуации, когда вторая открывающая скобка находится сразу после первого открытия скобки. Обычно в обычном классе есть некоторый код между двумя фигурными скобками, но это абсолютно одно и то же. Так что это вопрос размещения скобок. Поэтому я думаю, что мы не должны говорить, что это какая-то новая захватывающая вещь, потому что это то, что мы все знаем, но просто написано с некоторым кодом между скобками. Мы не должны создавать новую концепцию, называемую «инициализация двойной скобки».

Я не согласен с аргументом, что вы создаете слишком много анонимных классов. Вы не создаете их, потому что блок инициализации, но только потому, что вы их создаете. Они будут созданы, даже если вы не использовали две инициализации брекетов, чтобы эти проблемы возникали даже без инициализации ... Инициализация не является фактором, который создает инициализированный объект.

Кроме того, мы не должны говорить о проблеме, созданной используя эту несуществующую вещь «инициализацию двойной скобки» или даже обычную инициализацию одной скобки, поскольку описанные проблемы существуют только из-за создания анонимного класса, поэтому он не имеет ничего общего с исходным вопросом. Но все ответы дают читателям впечатление, что это не вина в создании анонимных классов, а эта злая (несуществующая) вещь, называемая «инициализация двойной скобки».

7
ответ дан ctomek 28 August 2018 в 06:15
поделиться

вы имеете в виду что-то вроде этого?

List<String> blah = new ArrayList<String>(){{add("asdfa");add("bbb");}};

это инициализация списка массивов во время создания (hack)

3
ответ дан dhblah 28 August 2018 в 06:15
поделиться

Каждый раз, когда кто-то использует инициализацию двойной скобки, котенка убивают.

Помимо синтаксиса, довольно необычного и не очень идиоматического (вкус, конечно, дискуссионный), вам необязательно создавать две существенные проблемы в вашем приложении , о котором я только что недавно подробно рассказал здесь .

1. Вы создаете слишком много анонимных классов

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

Map source = new HashMap(){{
    put("firstName", "John");
    put("lastName", "Smith");
    put("organizations", new HashMap(){{
        put("0", new HashMap(){{
            put("id", "1234");
        }});
        put("abc", new HashMap(){{
            put("id", "5678");
        }});
    }});
}};

... будет генерировать эти классы:

Test$1$1$1.class
Test$1$1$2.class
Test$1$1.class
Test$1.class
Test.class

Это довольно немного накладных расходов для вашего загрузчика классов - ни за что! Конечно, это не займет много времени для инициализации, если вы это сделаете один раз. Но если вы делаете это 20 000 раз по всему вашему корпоративному приложению ... все, что куча памяти только для немного «синтаксиса сахара»?

2. Вы потенциально создаете утечку памяти!

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

public class ReallyHeavyObject {

    // Just to illustrate...
    private int[] tonsOfValues;
    private Resource[] tonsOfResources;

    // This method almost does nothing
    public Map quickHarmlessMethod() {
        Map source = new HashMap(){{
            put("firstName", "John");
            put("lastName", "Smith");
            put("organizations", new HashMap(){{
                put("0", new HashMap(){{
                    put("id", "1234");
                }});
                put("abc", new HashMap(){{
                    put("id", "5678");
                }});
            }});
        }};

        return source;
    }
}

Возвращенный Map теперь будет содержать ссылку на экземпляр-экземпляр ReallyHeavyObject. Вероятно, вы не хотите рисковать этим:

Memory Leak Right Here [/g5]

Изображение из http://blog.jooq.org/2014/12/ 08 / dont-be-clever-the-double-curly-braces-anti-pattern /

3. Вы можете притворяться, что Java имеет литералы на карте

. Чтобы ответить на ваш реальный вопрос, люди использовали этот синтаксис, чтобы притворяться, что Java имеет что-то вроде картографических литералов, похожих на существующие литералы массивов:

String[] array = { "John", "Doe" };
Map map = new HashMap() {{ put("John", "Doe"); }};

Некоторые люди могут найти это синтаксически стимулирующее.

214
ответ дан Lukas Eder 28 August 2018 в 06:15
поделиться

Он - среди других применений - ярлык для инициализации коллекций. Подробнее ...

3
ответ дан miku 28 August 2018 в 06:15
поделиться
  • Первая скобка создает новый анонимный внутренний класс.
  • Второй набор фигурных скобок создает инициализаторы экземпляра, такие как статический блок в классе.

Например:

   public class TestHashMap {
    public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<String,String>(){
        {
            put("1", "ONE");
        }{
            put("2", "TWO");
        }{
            put("3", "THREE");
        }
        };
        Set<String> keySet = map.keySet();
        for (String string : keySet) {
            System.out.println(string+" ->"+map.get(string));
        }
    }

}

Как это работает

Первая скобка создает новый анонимный внутренний класс. Эти внутренние классы способны получить доступ к поведению своего родительского класса. Итак, в нашем случае мы фактически создаем подкласс класса HashSet, поэтому этот внутренний класс способен использовать метод add ().

И Второй набор фигурных скобок - это ничего, кроме инициализаторов экземпляров. Если вы напомните основные концепции Java, вы можете легко связать блоки инициализатора экземпляра со статическими инициализаторами из-за аналогичной структуры, подобной структуре. Единственное отличие состоит в том, что статический инициализатор добавляется с ключевым словом static и запускается только один раз; независимо от того, сколько объектов вы создаете.

more

35
ответ дан Premraj 28 August 2018 в 06:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: