Можно ли сделать анонимные внутренние классы в Java статическими?
В Java вложенные классы могут быть static
или нет. Если они static
они не содержат ссылку на указатель содержащего экземпляра (они также больше не называются внутренними классами, они называются вложенными классами).
Забыть сделать вложенный класс static
когда эта ссылка не нужна, это может привести к проблемам со сборкой мусора или анализом побега.
Можно ли сделать анонимный внутренний класс static
также? Или компилятор вычисляет это автоматически (что могло бы быть, потому что не может быть никаких подклассов)?
Например, если я создаю анонимный компаратор, мне почти никогда не нужна ссылка на внешнюю:
Collections.sort(list, new Comparator<String>(){
int compare(String a, String b){
return a.toUpperCase().compareTo(b.toUpperCase());
}
}
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 Глоссарий Роди Грина говорит, что тот факт, что анонимные классы разрешены в статическом контексте, зависит от реализации:
Если вы хотите, чтобы сбить с толку тех, кто поддерживает ваш код, виги обнаружили
javac.exe
разрешит анонимные занятия внутриstatic
код инициализации иstatic
методы, хотя спецификация языка говорит, что анонимные классы никогда не бываютstatic
, Эти анонимные классы, конечно, не имеют доступа к полям экземпляра объекта. Я не рекомендую делать это. Эта функция может быть удалена в любое время.
Редактировать 2: JLS фактически охватывает статические контексты более явно в §15.9.2:
Позвольте C быть экземпляром класса, и позвольте мне быть создаваемым экземпляром. Если C является внутренним классом, то у меня может быть немедленно включающий экземпляр. Непосредственно включающий экземпляр i (§8.1.3) определяется следующим образом.
- Если C является анонимным классом, то:
- Если выражение создания экземпляра класса происходит в статическом контексте (§8.1.3), то у меня нет немедленно включающего экземпляра.
- В противном случае немедленно включающий экземпляр i
this
,
Таким образом, анонимный класс в статическом контексте примерно эквивалентен static
вложенный класс в том смысле, что он не хранит ссылку на включающий класс, хотя технически это не static
учебный класс.
Я думаю, что здесь есть некоторая путаница в номенклатуре, которая, по общему признанию, слишком глупа и запутана.
Как бы вы их ни называли, эти шаблоны (и несколько вариантов с разной видимостью) - это все возможная, нормальная, легальная 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. Честно!
Вид. Анонимный внутренний класс, созданный в статическом методе, очевидно, будет эффективно статическим, потому что нет никакого источника для внешнего this.
Есть некоторые технические различия между внутренними классами в статических контекстах и статическими вложенными классами. Если вам интересно, прочитайте JLS 3rd Ed.
Внутренние классы не могут быть статичными - статический вложенный класс не является внутренним классом. Об этом рассказывается в руководстве по Java.
Анонимные внутренние классы никогда не являются статическими (они не могут объявлять статические методы или неконечные статические поля), но если они определены в статическом контексте (статический метод или статическое поле), они ведут себя как статические в том смысле, что не могут получить доступ к нестатическим (т.е. экземплярам) членам включающего класса (как и все остальное из статического контекста)
На заметку о создании статического анонимного внутреннего класса путем вызова их внутри статического метода.
Это на самом деле не удаляет ссылку. Вы можете проверить это, попытавшись сериализовать анонимный класс и не сделав включающий класс сериализуемым.