Почему анонимный класс в статическом контексте действителен
У меня есть недоразумение о том, что такое анонимный класс в 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
Внутренний класс - это вложенный класс, который явно или неявно объявленный статический.
Таким образом, анонимный класс является внутренним классом. Но мы используем их в статическом контексте. Почему здесь это правильно?
Ответы
Ответ 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
}
};
}
}
В вашем примере вы могли бы подумать, что создали статический анонимный внутренний класс, потому что вы его создали:
- из статического вложенного интерфейса
- в статическом контексте - поэтому без доступа к внешнему объекту
Но одно и то же объявление в нестационарном контексте доказывает, что статический вложенный интерфейс создает нестатический анонимный класс