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

Вложенные или внутренние классы (inner class) - это классы определенные внутри другого класса. Область действия вложенного класса ограничена областью действия внешнего класса. Если класс В определен в классе А, то класс B не может существовать независимо от класса А. Вложенный класс имеет доступ к членам (в том числе закрытым) того класса, в который он объявлен.

Типы вложенных классов:

  1. Обычный внутренний класс (regular inner class or member class).
  2. Локальный класс (method-local inner class).
  3. Анонимный класс (anonymous inner class).
  4. Статический вложенный класс (static nested class).

1. Обычный внутренний класс

Внутренний класс определяется в области действия внешнего класса.

Чтобы создать объект внутреннего класса, должен существовать объект внешнего класса.

Внутренний и внешний класс имеют доступ ко всем членам класса друг друга (даже private).

Следующий пример демонстрирует объявление обычного внутреннего класса:

public class Town {
    private String postCode = "33333";

    public class Street {
        private int house;

        public void printAddress() {
            System.out.println("Город " + Town.this);
            System.out.println("Индекс " + postCode);
            System.out.println("Улица " + this);
            System.out.println("Дом " + house);
        }
    }

    public void createStreet() {
        Street street = new Street();
        street.house = 78;
        street.printAddress();
    }

    public static void main(String[] args) {
        Town town = new Town();
        town.createStreet();
        Town.Street street1 = town.new Street();
        Town.Street street2 = new Town().new Street();
        street1.printAddress();
        street2.printAddress();
    }
}

Внутри метода внешнего класса, объект внутреннего класса создается как обычно:

Street street = new Street();

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

new Town().new Street(); 

или

town.new Street();

Если необходимо получить ссылку на внутренний класс во внутреннем классе, используем слово this:

System.out.println("Street is " + this);

Если необходимо получить ссылку на объект внешнего класса, запишите имя внешнего класса, за которым следует точка, а затем ключевое слово this:

System.out.println("Town is " + Town.this);

Обычный внутренний класс является таким же членом внешнего класса, как и переменные и методы. Следующие модификаторы могут быть применены к обычному внутреннему классу:

  • final
  • abstract
  • public
  • private
  • protected
  • static – но static преобразует его во вложенный класс
  • strictfp

Если метод описан как strictfp (явно либо неявно), то JVM гарантирует, что результаты вычисления выражений с double и float в пределах метода будут одинаковыми на всех платформах. Модификатор strictfp для класса и интерфейса указывает на то, что все методы класса/интерфейса будут strictfp.

2. Локальный класс Java 

Локальный класс (local class) определяется в блоке Java кода. На практике чаще всего объявление происходит в методе некоторого другого класса. Как и member классы, локальные классы ассоциируются с экземпляром внешнего класса и имеют доступ к его полям и методам.

Локальный класс может обращаться к локальным переменным и параметрам метода, если они объявлены с модификатором final или являются effective final(начиная с Java 8).

Effective final переменная это переменная, которая не объявлена явно как final, но ее значение не меняется.

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

Локальные классы не могут быть объявлены как private, public, protected или static.

Они не могут иметь внутри себя статических объявлений (полей, методов, классов). Исключением являются константы (static final).

Локальные классы могут быть объявлены как abstract или final.

Рассмотрим пример объявления локального класса:

public class Town2 {
    private String postCode = "33333";

    public void createAddress() {
        final int houseNumber = 34;
        class Street {
            public void printAddress() {
                System.out.println("PostCode is " + postCode);
                System.out.println("House Number is " + houseNumber);
            }
        }
        Street street = new Street();
        street.printAddress();
    }

    public static void main(String[] args) {
        Town2 town = new Town2();
        town.createAddress();
    }
}

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

public class Town3 {
    private static String postCode = "33333";

    public static void createAddress() {
        final int houseNumber = 34;
        class Street {
            public void printAddress() {
                System.out.println("PostCode is " + postCode);
                System.out.println("House Number is " + houseNumber);
            }
        }
        Street street = new Street();
        street.printAddress();
    }

    public static void main(String[] args) {
        Town3.createAddress();
    }
}

3. Анонимный класс

Анонимный класс (anonymous class) - это локальный класс без имени. Используется тогда, когда нужно переопределить метод класса или интерфейса. Класс одновременно объявляется и инициализируется.

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

Рассмотрим пример анонимного класса:

public class Potato {
    public void peel() {
        System.out.println("Чистим картошку.");
    }
}

public class Food {
    public static void main(String[] args) {
        Potato potato = new Potato() {
            @Override
            public void peel() {
                System.out.println("Чистим картошку в анонимном классе.");
            }
        };
        potato.peel();
    }
}

Анонимный класс может не только переопределить методы класса наследника, но и добавить новые методы. Но новые методы НЕ могут быть вызваны извне анонимного класса:

public class AnotherFood {
    public static void main(String[] args) {
        Potato potato = new Potato() {
            public void fry() {
                System.out.println("Жарим картошку в анонимном классе.");
            }

            @Override
            public void peel() {
                System.out.println("Чистим картошку в анонимном классе.");
                fry();
            }
        };
        potato.peel();
        //Ошибка компиляции
        //potato.fry();
    }
}

Случаи использования анонимного класса:

  • Тело класса является очень коротким.
  • Нужен только один экземпляр класса.
  • Класс используется в месте его создания или сразу после него.
  • Имя класса не важно и не облегчает понимание кода.

Анонимный класс могут также расширять интерфейс:

public interface Moveable {
    void moveRight();
    void moveLeft();
}

public class MoveableDemo {
    public static void main(String[] args) {
        Moveable moveable = new Moveable() {
            @Override
            public void moveRight() {
                System.out.println("MOVING RIGHT!!!");
            }

            @Override
            public void moveLeft() {
                System.out.println("MOVING LEFT!!!");
            }
        };
        moveable.moveRight();
        moveable.moveLeft();
    }
}

4. Статический вложенный класс

Статический вложенный класс (static nested class) – это внутренний класс объявленный с модификатором static.

Статический вложенный класс не имеет доступа к нестатическим полям и методам внешнего класса. Доступ к нестатическим полям и методам может осуществляться только через ссылку на экземпляр внешнего класса. В этом плане static nested классы очень похожи на любые другие классы верхнего уровня.

Рассмотрим примеры объявления статических вложенных классов:

public class Town4 {
    public static class Street {
        public void go() {
            System.out.println("Go to the Street.");
        }
    }
}

public class City {
    public static class District {
        public void go() {
            System.out.println("Go to the District.");
        }
    }

    public static void main(String[] args) {
        Town4.Street street = new Town4.Street(); 
        street.go();
        District district = new District(); 
        district.go();
    }
}
Read also:
Comments