Вложенные классы - 1

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

— Привет, Элли!

— Сегодня будет очень интересная тема. Я сегодня расскажу о внутренних классах.

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

Объекты внутренних классов при этом вложены в объекты внешних классов и могут обращаться к переменным внешних классов.

Пример
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:

Как получить доступ к this внешнего (или любого) класса
Car.this
Car.Door.this
Car.Door.InnerClass2.InnerClass3.this

— Т.е. если мы внутри метода внутреннего класса пишем this, то этот this относится к внутреннему классу?

— Да. Именно так.

Как тебе внутренние классы, Амиго?

— Очень интересно. Я бы не сказал, что очень сложно.

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

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

Спасибо за отличный урок, Элли.

— Рада, что тебе понравилось, Амиго.