Что происходит на самом деле (Во что превращаются классы компилятором) - 1

— Привет, Амиго! Вот тебе еще немного информации.

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

— Ага. Я даже помню, что у них имена – это числа: 1, 2, 3 и т.д.

— Именно так. Но вот еще какой есть нюанс.

Если класс был объявлен внутри метода и использовал какие-то переменные, то ссылки на них будут добавлены в сгенерированный класс. Смотри сам:

Было:

Исходный пример:
class Car
{
 public ArrayList<Car> createPoliceCars(int count)
 {
  ArrayList<Car> result = new ArrayList<Car>();

  for(int i=0; i<count; i++)
  {
   final int number = i;
   result.add(new Car()
    {
     public String toString()
     {
      return ""+number;
     }
    });
  }
  return result;
 }
}

Результат компиляции:

Что сгенерировал компилятор:
class Car
{
 public ArrayList<Car> createPoliceCars(int count)
 {
  ArrayList<Car> result = new ArrayList<Car>();

  for(int i=0; i<count; i++)
  {
   final int number = i;
   result.add(new Anonymous2(number));
  }
   return result;
  }

 class Anonymous2
 {
  final int number;
  Anonymous2(int number)
 {
  this.number = number;
 }

  public String toString()
  {
   return ""+number;
  }
 }
}

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

Теперь второй момент. Метод toString() использует переданную переменную. Для этого пришлось:

А) сохранить ее внутри сгенерированного класса

Б) добавить ее в конструктор.

— Понял. Классы, объявленные внутри метода, всегда работают с копией переменных.

— Именно!

— Тогда понятно, почему переменные должны быть final. И почему их нельзя менять. Если на самом деле ты работаешь с копией, а не с оригиналом, то пользователь не поймет, почему не может менять значение переменной, а значит, надо просто ему запретить ее менять.

— Да, мне кажется, что объявление переменных final – это небольшая плата за то, что компилятор за тебя сгенерирует класс, передаст в него и сохранит там все переменные метода, которые ты хочешь использовать.

— Согласен. Все-таки это крутая штука – анонимные локальные классы.

А если я объявлю внутри метода свой локальный класс и буду в нем использовать переменные метода, компилятор их тоже добавит этому классу?

— Да, добавит в класс и его конструктор.

— Я так и думал.