Можно ли сделать анонимные внутренние классы в Java статическими?

123

В Java вложенные классы могут быть либо, staticлибо нет. Если да static, то они не содержат ссылки на указатель содержащего экземпляра (они также больше не называются внутренними классами, они называются вложенными классами).

Если вы забудете создать вложенный класс, staticкогда ему не нужна эта ссылка, это может привести к проблемам со сборкой мусора или анализом выхода.

Можно ли сделать также анонимный внутренний класс static? Или компилятор вычисляет это автоматически (что могло бы быть, потому что не может быть никаких подклассов)?

Например, если я сделаю анонимный компаратор, мне почти никогда не понадобится ссылка на внешнюю сторону:

  Collections.sort(list, new Comparator<String>(){
       int compare(String a, String b){
          return a.toUpperCase().compareTo(b.toUpperCase());
       }
  }
Тило
источник
Каковы проблемы с «сборкой мусора или анализом выхода», когда вы забываете сделать внутренний класс статическим? Я думал, что речь идет только о производительности ...
Тим Бют
17
Экземпляр вашего внутреннего класса сохраняет ссылку на свой внешний экземпляр, даже если он вам не нужен. Это может предотвратить сборку мусора. Представьте себе (ресурсоемкий) фабричный объект, который создает легкие экземпляры чего-либо. После того, как фабрика выполнила свою работу (например, во время запуска приложения), ее можно было бы удалить, но это работает только в том случае, если созданные ею вещи не имеют обратной связи.
Thilo
Я знаю, что это всего лишь пример, но, поскольку он повторяющийся, следует упомянуть, что он Collections.sort(list, String.CASE_INSENSITIVE_ORDER)работает с Java 2, читайте, поскольку существует Collection API…
Холгер

Ответы:

138

Нет, вы не можете, и нет, компилятор не может этого понять. Вот почему FindBugs всегда предлагает заменить анонимные внутренние классы именованными staticвложенными классами, если они не используют свою неявную thisссылку.

Изменить: Tom Hawtin - tackline говорит, что если анонимный класс создается в статическом контексте (например, в mainметоде), анонимный класс на самом деле static. Но JLS не согласен :

Анонимный класс никогда не бывает abstract(§8.1.1.1). Анонимный класс всегда является внутренним классом (§8.1.3); это никогда не бывает static(§8.1.1, §8.5.1). Анонимный класс всегда неявно final(§8.1.1.2).

В глоссарии Java Roedy Green говорится, что тот факт, что анонимные классы разрешены в статическом контексте, зависит от реализации:

Если вы хотите сбить с толку тех, кто поддерживает ваш код, обнаруженные шутки javac.exeразрешат анонимные классы внутри staticкода и staticметодов инициализации , даже если спецификация языка говорит, что анонимные классы никогда не допускаются static. Эти анонимные классы, конечно же, не имеют доступа к полям экземпляра объекта. Я не рекомендую это делать. Функция может потянуться в любое время.

Изменить 2: JLS фактически охватывает статические контексты более явно в §15.9.2 :

Пусть C будет экземпляром класса, и пусть я будет создаваемым экземпляром. Если C - внутренний класс, тогда у меня может быть экземпляр, непосредственно включающий его. Непосредственно включающий экземпляр i (§8.1.3) определяется следующим образом.

  • Если C - анонимный класс, то:
    • Если выражение создания экземпляра класса встречается в статическом контексте (§8.1.3), то i не имеет экземпляра, непосредственно включающего его.
    • В противном случае непосредственно включающий экземпляр i будет this.

Таким образом, анонимный класс в статическом контексте примерно эквивалентен staticвложенному классу в том смысле, что он не сохраняет ссылку на включающий класс, хотя технически это не staticкласс.

Майкл Майерс
источник
19
+1 за FindBugs - каждый Java-разработчик должен иметь это в своей сборке.
Эндрю Даффи,
13
Это очень прискорбно, потому что это означает, что вы можете избежать этого, в противном случае, почти сжатого синтаксиса из соображений производительности.
Тило
2
В 3-м издании JLS рассматривается случай внутренних классов в статических контекстах. Они не статичны в смысле JLS, но статичны в смысле, заданном в вопросе.
Том Хотин - tackline
6
Вот пример того, как это зависит от реализации: этот код печатается trueс использованием javac (sun-jdk-1.7.0_10) и falseкомпилятора Eclipse.
Пол Беллора
1
@MichaelMyers Я попытался смоделировать FindBugs, предупреждающий меня о выполнении анонимного внутреннего кода без использования ссылки this, и ничего не происходит. Можете ли вы продемонстрировать, как FindBugs предупреждает вас, как вы сказали в начале своего ответа? Просто вставьте ссылку или что-нибудь еще.
Туфир Хават
15

Вид. Анонимный внутренний класс, созданный в статическом методе, очевидно, будет фактически статическим, потому что нет источника для внешнего this.

Между внутренними классами в статических контекстах и ​​статическими вложенными классами есть некоторые технические различия. Если вам интересно, прочтите JLS 3rd Ed.

Том Хотин - tackline
источник
Собственно, я беру это обратно; JLS не согласен. java.sun.com/docs/books/jls/third%5Fedition/html/… : «Анонимный класс всегда является внутренним классом; он никогда не бывает статичным».
Майкл Майерс
1
static в другом смысле, чем в вопросе.
Том Хотин - tackline
1
Я добавил небольшое пояснение.
Том Хотин - tackline
15

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

Как бы вы их ни называли, эти шаблоны (и несколько вариантов с разной видимостью) - все возможные, нормальные, законные Java:

public class MyClass {
  class MyClassInside {
  }
}

public class MyClass {
  public static class MyClassInside {
  }
}

public class MyClass {
  public void method() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

public class MyClass {
  public static void myStaticMethod() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

Они учтены в спецификации языка (если вас действительно беспокоит, см. Раздел 15.9.5.1, посвященный статическому методу).

Но эта цитата просто неверна :

javac.exe разрешит анонимные классы внутри статического кода инициализации и статических методов, хотя в спецификации языка сказано, что анонимные классы никогда не являются статическими

Я думаю, что цитируемый автор путает ключевое слово static со статическим контекстом . (Следует признать, что JLS также немного сбивает с толку в этом отношении.)

Честно говоря, все вышеперечисленные шаблоны хороши (как бы вы их ни называли «вложенными», «внутренними», «анонимными» как угодно ...). Действительно, в следующем выпуске Java никто не собирается вдруг убирать эту функциональность. Честно!

Нил Коффи
источник
2
«(Следует признать, что JLS также немного сбивает с толку в этом отношении.)« Вы правильно поняли. Было бы странно сказать, что это зависит от реализации, но я не припомню, чтобы раньше видел какие-либо очевидные ошибки в глоссарии Java. С этого момента я отношусь к этому с недоверием.
Майкл Майерс
2
На самом деле мы не говорим ни о каких шаблонах. Мы имеем в виду, что анонимный вложенный класс статичен. Т.е. добавьте «статику» между newи JComponentв третьем примере.
Timmmm
Я добавил пояснение к исходному вопросу, чтобы показать, что от него требуется.
Timmmm
@MichaelMyers, Диктовку в JLS всегда нужно интерпретировать.
Pacerier
6

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

Эндрю Даффи
источник
1
Я обновил вопрос со ссылкой на официальную номенклатуру.
Тило
0

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

Лука
источник
-3

Обратите внимание на то, что анонимный внутренний класс становится статическим, вызывая его внутри статического метода.

Это фактически не удаляет ссылку. Вы можете проверить это, попытавшись сериализовать анонимный класс, не делая сериализуемый включающий класс.

Терра Кейнс
источник
5
-1: Создание анонимного класса внутри метода статического фактически делает удалить ссылку на внешний класс. Вы можете проверить это, попытавшись сериализовать анонимный класс, не делая сериализуемый включающий класс. (Я только что сделал.)
Christian Semrau