Подтвердить что ты не робот

Почему анонимный класс в статическом контексте действителен

У меня есть недоразумение о том, что такое анонимный класс в Java. Рассмотрим следующий простой пример:

public static void main (String[] args) throws java.lang.Exception
{
    B b = new B(){ };
    System.out.println(b.b);
}

interface B{ int b = 1; }

DEMO

Почему компиляция кода? JLS, глава 15 говорит:

Анонимный класс всегда является внутренним классом (§8.1.3); это никогда статические

Но JLS, chapt 8

Внутренний класс - это вложенный класс, который явно или неявно объявленный статический.

Таким образом, анонимный класс является внутренним классом. Но мы используем их в статическом контексте. Почему здесь это правильно?

4b9b3361

Ответ 1

Класс может быть создан в статическом контексте, не будучи объявленным статическим, и это то, что происходит здесь. Давайте посмотрим, что объявляется статическим и создается в статическом контексте:

Разница между анонимным классом, созданным в статическом контексте и нестационарным контекстом, заключается в том, имеет ли он экземпляр-экземпляр:

Если C - анонимный класс, то:

  • Если выражение создания экземпляра класса встречается в статическом контексте, то у я нет непосредственного входящего экземпляра.

  • В противном случае непосредственным экземпляром я является тот.

Вложенный класс, объявленный static, позволяет статическим членам:

Внутренний класс представляет собой вложенный класс, который явно или неявно не объявлен статическим.

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

Говоря вложенным классом, который является "implicity объявлен статическим", он ссылается на такие вещи, как классы внутри интерфейсов:

Класс-член интерфейса неявно статичен (§9.5), поэтому никогда считается внутренним классом.

Анонимные классы не объявляются статическими (ни явным образом с ключевым словом, либо неявно, например, внутри интерфейса), и поэтому не допускают объявления статических членов. Однако они могут быть созданы в статическом контексте, а это означает, что они не относятся к закрывающему экземпляру.

Поскольку анонимные классы не объявляются статическими, обе кавычки в вопросе согласованы.

Ответ 2

Вы должны различать анонимные и внутренние классы

С помощью этого утверждения вы создаете класс, реализующий интерфейс B. Класс не имеет имени, поэтому он называется анонимным классом. Компилятор javac создаст файл класса со следующим правилом именования YourClass $1.class(где 1 - порядковый номер, основанный на количестве анонимных классов в YourClass.

B b = new B(){ };

Класс, созданный new B(){ }, не может быть объявлен как статический. ( "Анонимный класс всегда является внутренним классом (§8.1.3), он никогда не является статичным" ).

Пример статического вложенного класса

class YourClass {
    static class StaticNestedClass {
    }
}

Пример нестатического вложенного класса

class YourClass {
    // An inner class is a nested class that is not explicitly or implicitly declared static.
    class InnerClass {
    }
}

Существует два типа вложенных классов: статические и нестатические. Вложенные классы, объявленные статическими, являются так называемыми static nested classes, тогда как вложенные классы, не объявленные как статические, являются так называемыми inner classes.

Ответ 3

B - внутренний интерфейс. Поскольку такой B неявно статичен, и вы можете ссылаться на B в статическом контексте. (Из JLS 8.5.1: Интерфейс элемента неявно статичен (§9.1.1).)

Но анонимный класс, созданный через B, не является статическим, и если он ссылается на нестационарный контекст, вы можете получить доступ к внешнему объекту:

public class Main{

    public static void main(String[] args) throws java.lang.Exception {
        B b = new B() {

            @Override
            public Ess3 getParent() {
                //return Ess3.this;  ERROR : non static variable cannot be referenced from static context
                return null;
            }
        };
        System.out.println(b.b);
    }

    interface B {

        int b = 1;
        Ess3 getParent();
    }

    /* a non static method */
    void foo() {
        B b = new B() {

            @Override
            public Ess3 getParent() {
                return Ess3.this; // this anonymous class is not static
            }

        };

    }
}

В вашем примере вы могли бы подумать, что создали статический анонимный внутренний класс, потому что вы его создали:

  • из статического вложенного интерфейса
  • в статическом контексте - поэтому без доступа к внешнему объекту

Но одно и то же объявление в нестационарном контексте доказывает, что статический вложенный интерфейс создает нестатический анонимный класс