— Привіт, Аміго!

— Сьогодні буде дуже цікава тема. Я розповім про внутрішні класи.

Якщо один клас оголосити усередині іншого, то такий клас називається внутрішнім.

Об'єкти внутрішніх класів при цьому вкладені в об'єкти зовнішніх класів і можуть звертатися до змінних внутрішніх класів.

Приклад
public class Car
{
 int height = 160;
 ArrayList doors = new ArrayList();

 public Car
 {
  doors.add(new Door());
  doors.add(new Door());
  doors.add(new Door());
  doors.add(new Door());
 }

class Door()
 {
  public int getDoorHeight()
  {
   return (int)(height * 0.80);
  }
 }
}

Зверни увагу: у класу Door(двері) є метод, який повертає висоту дверей – getDoorHeight: цей метод використовує змінну height об'єкта Car.

Вкладені класи - 2

Об'єкт типу Door не може існувати окремо від об'єкта типу Car, оскільки він використовує його змінні. Компілятор непомітно додає до конструктора і класу Door посилання на об'єкт зовнішнього класу Car, щоб методи внутрішнього класу Door могли звертатися до змінних і викликати методи зовнішнього класу Car.

— Вкладені об'єкти. Мені зрозуміло. З огляду на картинку тут все елементарно.

— Так і є. Лиши пара нюансів.

У внутрішньому класі Door є посилання на об'єкт класу Car, тому:

1) Не можна створювати об'єкт Door всередині статичного методу у класі Car: ніде взяти посилання на об'єкт типу Car, який неявно передається у конструктор типу Door.

Правильно Неправильно
public class Car
{
 public static Door createDoor()
 {
  Car car = new Car();
  return car.new Door();
 }

 public class Door
 {
  int width, height;
 }
}
public class Car
{
 public static Door createDoor()
 {
  return new Door();
 }

 public class Door
 {
  int width, height;
 }
}

2) Клас Door не може містити статичні змінні і методи.

Правильно Неправильно
public class Car
{
 public int count;
 public int getCount()
 {
  return count;
 }

 public class Door
 {
  int width, height;
 }
}
public class Car
{

 public class Door
 {
  public static int count;
  int width, height;

  public static int getCount()
  {
   return count;
  }
 }
}

— А якщо мені потрібна загальна змінна для всіх об'єктів Door?

Ти завжди можеш оголосити її просто у класі Car – це і буде загальна змінна для всіх об'єктів Door, вкладених у об'єкт Car.

3) Зверни увагу, якщо внутрішній клас оголошено як public, його об'єкти можна створювати поза зовнішнім класом, але об'єкт зовнішнього класу повинен бути присутнім:

Car car = new Car();
Car.Door door = car.new Door();
Car.Door door = new Car().new Door();

4) Ще одне зауваження – мало не забув.

Оскільки ми маємо два вкладених об'єкти, то у методах внутрішнього об'єкта доступно два посилання this:

public class Car
{
 int width, height;

 public class Door
 {
  int width, height;

  public void setHeight(int height)
  {
   this.height = height;
  }

  public int getHeight()
  {
   if (height!=0)
    return this.height;
   else
    return (int)(Car.this.height * 0.8);
  }
 }
}

Ми спеціально оголосили в класах однакові змінні.

Щоб звернутися до змінної зовнішнього класу, коли вона прихована, або до цього зовнішнього класу, достатньо написати «ім'я класу» крапка this:

Як отримати доступ до this зовнішнього (або будь-якого) класу
Car.this
Car.Door.this
Car.Door.InnerClass2.InnerClass3.this

— Тобто якщо ми всередині методу внутрішнього класу пишемо this, цей this відноситься до внутрішнього класу?

— Так. Саме так.

Як тобі внутрішні класи, Аміго?

— Дуже цікаво. Я не сказав би, що дуже складно.

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

А я вже два місяці в практичних завданнях постійно пишу вкладені класи і тільки зараз зрозумів, що я писав насправді.

Дякую за чудовий урок.

— Добре, що тобі сподобалось, Аміго.