Чи можливо зробити анонімні внутрішні класи в 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
Ваш внутрішній екземпляр класу зберігає посилання на його зовнішній екземпляр, навіть якщо він вам не потрібен. Це може запобігти збиранню сміття. Зображте (важкий для ресурсів) заводський об'єкт, який створює легкі екземпляри чогось. Після того, як фабрика виконала свою роботу (наприклад, під час запуску програми), її можна було утилізувати, але це працює лише в тому випадку, якщо створені вами речі не зв'язуються назад.
Тіло

Я знаю, це лише приклад, але оскільки він є повторюваним, слід згадати, що він Collections.sort(list, String.CASE_INSENSITIVE_ORDER)працює з Java 2, читайте, оскільки API API колекції існує…
Holger

Відповіді:


138

Ні, ви не можете, і ні, компілятор не може це зрозуміти. Ось чому 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методів init , хоча мовна специфікація говорить, що анонімні класи ніколиstatic . Ці анонімні класи, звичайно, не мають доступу до полів екземпляра об'єкта. Я не рекомендую цього робити. Функція може потягнутися в будь-який час.

Редагувати 2: JLS фактично охоплює статичні контексти більш чітко в § 15.9.2 :

Нехай C - клас, який інстанціюється, і я буду створеним екземпляром. Якщо C - внутрішній клас, то мене може бути екземпляр, що безпосередньо вкладається. Одразу додається екземпляр i (§ 8.1.3) визначається наступним чином.

  • Якщо C - анонімний клас, то:
    • Якщо вираз створення екземпляра класу відбувається в статичному контексті (§8.1.3), то мене немає екземпляра, що безпосередньо прикладається.
    • В іншому випадку, безпосередньо додається екземпляр i є this.

Отже, анонімний клас у статичному контексті приблизно еквівалентний staticвкладеному класу, оскільки він не зберігає посилання на клас, що додається, хоча технічно це не staticклас.


19
+1 для FindBugs - кожен розробник Java повинен мати це у своїй збірці.
Ендрю Даффі

13
Це дуже прикро, бо це означає, що ви можете захотіти уникати цього інакше майже стислого синтаксису з міркувань продуктивності.
Тило

2
JLS 3rd Ed розглядає справи внутрішніх класів у статичних контекстах. Вони не є статичними в сенсі JLS, але є статичними у тому сенсі, який задано у питанні.
Том Хотін - тайклін

6
Ось приклад того, як це залежить від реалізації: цей код друкується trueза допомогою javac (sun-jdk-1.7.0_10) та falseза допомогою компілятора Eclipse.
Пол Беллора

1
@MichaelMyers Я намагався імітувати FindBugs, попереджуючи мене про те, що я роблю Anonymous Inner, не використовуючи посилання "це", і нічого не відбувається. Чи можете ви продемонструвати, як FindBugs попереджає вас, як ви сказали на початку своєї відповіді? Просто вставте якесь посилання або що-небудь коли-небудь.
Туфір Хават

15

Типу. Анонімний внутрішній клас, створений статичним методом, очевидно, буде ефективно статичним, оскільки немає джерела для зовнішнього цього.

Існують деякі технічні відмінності між внутрішніми класами в статичних контекстах і статичними вкладеними класами. Якщо вам цікаво, прочитайте JLS 3rd Ed.


Власне, я повертаю це назад; JLS не погоджується. java.sun.com/docs/books/jls/third%5Fedition/html/… : "Анонімний клас - це завжди внутрішній клас; він ніколи не є статичним."
Майкл Майерс

1
статичний в іншому значенні, ніж у питанні.
Том Хотін - тайклін

1
Я додав трохи уточнень.
Том Хотін - тайклін

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 дозволить анонімним класам всередині статичного коду init та статичних методів, навіть якщо мовна специфікація говорить, що анонімні класи ніколи не є статичними

Я думаю, що цитований автор плутає статичне ключове слово зі статичним контекстом . (Правда, JLS також трохи заплутаний у цьому відношенні.)

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


2
"(Правда, JLS також трохи заплутаний у цьому відношенні.)" Ви отримали це право. Дуже дивно говорити, що це залежить від реалізації, але я не пригадую, щоб раніше в язиковому словнику Java були явні помилки. Відтепер я беру його із зерном солі.
Майкл Майєрс

2
Ми фактично не говоримо ні про яку з закономірностей. Ми маємо на увазі, що анонімний вкладений клас є статичним. Тобто додати «статичний» між newі JComponentв вашому третьому прикладі.
Тимммм

До оригінального питання я додав уточнення, щоб показати, що потрібно.
Timmmm

@MichaelMyers, Диктант в JLS завжди потрібно тлумачити.
Pacerier


0

анонімні внутрішні класи ніколи не є статичними (вони не можуть оголосити статичні методи або не остаточні статичні поля), але якщо вони визначені в статичному контексті (статичний метод або статичне поле), вони поводяться як статичні в тому сенсі, що не можуть отримати доступ до нестатичних (тобто екземплярів) членів класу, що вкладається (як і все інше зі статичного контексту)


-3

Зауважив зробити анонімний внутрішній клас статичним, викликаючи їх у статичному методі.

Це фактично не видаляє посилання. Ви можете перевірити це, намагаючись серіалізувати анонімний клас і не роблячи додаючий клас серіалізаційним.


5
-1: Створення анонімного класу всередині методу статичного фактично робить видалити посилання на зовнішній клас. Ви можете перевірити це, намагаючись серіалізувати анонімний клас і не роблячи додаючий клас серіалізаційним. (Я щойно зробив.)
Крістіан Семрау
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.