Можно ли создать экземпляр анонимного класса в конструкторе внешнего класса?

У меня есть следующий код:

public class Outer {
     public Interface Anony {
         public void callback();
     }

     public Outer() {
        OtherClass.foo(new Anony() {
            @Override
            public void callback() {
                ....
            }
        });
    }
}

Но мой друг сказал мне, что в нем есть какая-то проблема. Я создал экземпляр анонимного класса в конструкторе Outer, поэтому экземпляр анонимного класса неявно ссылается на экземпляр класса Outer, т.е. Outer.this. Но в данный момент экземпляр Outer еще не был полностью создан. Таким образом, экземпляр анонимного класса ссылается на объект с неполными состояниями, поэтому проблема.

Он прав? Спасибо.

Ответ 1

Вы, друг, прав, но это зависит от использования курса.

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

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

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

Ответ 2

Вы можете это сделать, но не должны.

Это пример анти-шаблона, известный по-разному, как "разрешение this справиться с конструктором" - передача ссылки на объект, который создается другим классом изнутри конструктора. Причина, по которой вы не должны этого делать, заключается в том, что в многопоточной среде класс, которому была передана эта ссылка, может видеть новый объект в частично построенном и, следовательно, несогласованном состоянии. Это может привести к странным и труднодоступным ошибкам. Эта статья статьи от IBM является одним из многих ее описаний.

Что неясно, как это происходит здесь: Анонимные классы - это, фактически, внутренние классы, поэтому они содержат ссылку на содержащий класс (т.е. this). Класс-получатель OtherClass может видеть и даже действовать, this до завершения построения.

Ответ 3

Я построил мозговой пример.. всех возможных возможностей, о которых я мог думать:

class OtherClass
{
  public static void foo (final Anony x)
  {
    x.callback ();
  }
}

public class Outer
{
  public interface Anony
  {
    void callback ();
  }

  public class Inner implements Anony
  {
    public void callback ()
    {
      System.out.println ("Inner.callback");
    }
  }

  public class InnerDerived implements Anony
  {
    public void callback ()
    {
      System.out.println ("InnerDerived.callback");
    }
  }

  public static class StaticInner implements Anony
  {
    public void callback ()
    {
      System.out.println ("StaticInner.callback");
    }
  }

  public Outer ()
  {
    OtherClass.foo (new Anony ()
    {
      public void callback ()
      {
        System.out.println ("Anony.callback");
      }
    });
    OtherClass.foo (new Inner ());
    OtherClass.foo (new Inner ()
    {
      @Override
      public void callback ()
      {
        System.out.println ("Anony.Inner.callback");
      }
    });
    OtherClass.foo (new InnerDerived ());
    OtherClass.foo (new InnerDerived ()
    {
      @Override
      public void callback ()
      {
        System.out.println ("Anony.InnerDerived.callback");
      }
    });
    OtherClass.foo (new StaticInner ());
    OtherClass.foo (new StaticInner ()
    {
      @Override
      public void callback ()
      {
        System.out.println ("Anony.StaticInner.callback");
      }
    });
  }
}

Ожидаемый результат должен быть:

Anony.callback
Inner.callback
Anony.Inner.callback
InnerDerived.callback
Anony.InnerDerived.callback
StaticInner.callback
Anony.StaticInner.callback