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

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

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

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

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

  Collections.sort(list, new Comparator<String>(){
       int compare(String a, String b){
          return a.toUpperCase().compareTo(b.toUpperCase());
       }
  }

person Thilo    schedule 17.04.2009    source источник
comment
Каковы проблемы со сборкой мусора или анализом выхода, если вы забыли сделать внутренний класс статическим? Я думал, что речь идет только о производительности ...   -  person Tim Büthe    schedule 06.07.2009
comment
Экземпляр вашего внутреннего класса сохраняет ссылку на свой внешний экземпляр, даже если он вам не нужен. Это может предотвратить сборку мусора. Представьте себе (ресурсоемкий) фабричный объект, который создает легкие экземпляры чего-либо. После того, как фабрика выполнила свою работу (например, во время запуска приложения), ее можно было бы удалить, но это работает только в том случае, если созданные ею вещи не имеют обратной связи.   -  person Thilo    schedule 06.07.2009
comment
Я знаю, что это всего лишь пример, но, поскольку он повторяющийся, следует упомянуть, что Collections.sort(list, String.CASE_INSENSITIVE_ORDER) работает с Java 2, читайте, поскольку существует Collection API ...   -  person Holger    schedule 16.01.2017


Ответы (6)


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

Изменить: Том Хотин - 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 будет экземпляром класса, а i будет создаваемым экземпляром. Если C является внутренним классом, тогда i может иметь непосредственно включающий экземпляр. Непосредственно включающий экземпляр i (§8.1.3) определяется следующим образом.

  • If C is an anonymous class, then:
    • If the class instance creation expression occurs in a static context (§8.1.3), then i has no immediately enclosing instance.
    • В противном случае непосредственно включающий экземпляр i будет this.

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

person Michael Myers    schedule 17.04.2009
comment
+1 за FindBugs - каждый Java-разработчик должен иметь это в своей сборке. - person Andrew Duffy; 17.04.2009
comment
Это очень прискорбно, потому что это означает, что вы можете избежать этого, в противном случае, почти сжатого синтаксиса из соображений производительности. - person Thilo; 17.04.2009
comment
В 3-м издании JLS рассматривается случай внутренних классов в статических контекстах. Они не статичны в смысле JLS, но статичны в смысле, заданном в вопросе. - person Tom Hawtin - tackline; 17.04.2009
comment
вы можете захотеть избежать этого, иначе синтаксис будет почти кратким из соображений производительности. И этот синтаксис якобы становится действительно кратким в Java 8. - person Thilo; 21.09.2012
comment
@Thilo: Я давно не следил за новостями Java; что изменилось в Java 8? - person Michael Myers; 21.09.2012
comment
Однажды вы сможете написать Collections.sort(list, (Integer x, Integer y) -> x - y ); (или что-то очень похожее). По-прежнему делает то же самое, что и раньше (реализация анонимного компаратора), но гораздо реже печатает. - person Thilo; 22.09.2012
comment
Вот пример того, как это зависит от реализации: этот код печатает true с использованием javac (sun-jdk-1.7.0_10) и false с использованием компилятора Eclipse. - person Paul Bellora; 18.04.2013
comment
и должна ли будущая версия Java позволять создавать статический анонимный класс / лямбда? - person Pinch; 13.11.2015
comment
@MichaelMyers Я попытался смоделировать FindBugs, предупреждающий меня о выполнении анонимного внутреннего кода без использования ссылки this, и ничего не происходит. Можете ли вы продемонстрировать, как FindBugs предупреждает вас, как вы сказали в начале своего ответа? Просто вставьте ссылку или что-нибудь еще. - person G Bisconcini; 17.11.2015
comment
Вы не можете цитировать произвольные блоги, такие как «Глоссарий Java». Это не нормативные ссылки, и эта конкретная ссылка сверху вниз пронизана ошибками, которые он просто отказывается исправлять. Я пробовал. «Функцию можно будет вытащить в любой момент» - это просто догадки. Ваши цитаты JLS ясно показывают, что это поддерживаемая функция. Тебе следует удалить все это, а также прочее Tackline. Ошибки других людей не представляют интереса. - person user207421; 26.03.2019
comment
@PaulBellora, i не имеет непосредственно включающего экземпляра не зависит от реализации. вот что важно с точки зрения утечек памяти. - person Pacerier; 09.05.2020

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

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

person Tom Hawtin - tackline    schedule 17.04.2009
comment
Собственно, я беру это обратно; JLS не согласен. java.sun.com/docs/ books / jls / third% 5Fedition / html /: анонимный класс всегда является внутренним классом; он никогда не бывает статичным. - person Michael Myers; 17.04.2009
comment
static в другом смысле, чем в вопросе. - person Tom Hawtin - tackline; 17.04.2009
comment
Я добавил небольшое пояснение. - person Tom Hawtin - tackline; 17.04.2009

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

Как бы вы их ни называли, эти шаблоны (и несколько вариантов с разной видимостью) - это все возможные, нормальные, законные 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 разрешит анонимные классы внутри статического кода инициализации и статических методов, хотя в спецификации языка сказано, что анонимные классы никогда не являются статическими.

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

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

person Neil Coffey    schedule 17.04.2009
comment
(По общему признанию, JLS также немного сбивает с толку в этом отношении.) Вы правильно поняли. Было бы странно сказать, что это зависит от реализации, но я не припомню, чтобы раньше видел какие-либо очевидные ошибки в глоссарии Java. С этого момента я отношусь к этому с недоверием. - person Michael Myers; 17.04.2009
comment
На самом деле мы не говорим ни о каких закономерностях. Мы имеем в виду, что анонимный вложенный класс статичен. Т.е. добавьте статику между new и JComponent в третьем примере. - person Timmmm; 21.09.2012
comment
Я добавил пояснение к исходному вопросу, чтобы показать, что от него требуется. - person Timmmm; 21.09.2012
comment
@MichaelMyers, Диктовку в JLS всегда нужно интерпретировать. - person Pacerier; 09.05.2020

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

person Andrew Duffy    schedule 17.04.2009
comment
Я обновил вопрос со ссылкой на официальную номенклатуру. - person Thilo; 17.04.2009

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

person Luca    schedule 02.02.2016

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

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

person Terra Caines    schedule 21.03.2011
comment
-1: Создание анонимного класса внутри статического метода фактически действительно удаляет ссылку на внешний класс. Вы можете проверить это, попытавшись сериализовать анонимный класс, не делая сериализуемый включающий класс. (Я только что сделал.) - person Christian Semrau; 25.06.2011