Урок 15

Внутренние классы


1. Что такое внутренний класс

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

Типы внутренних классов:

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

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

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

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

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

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

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

    public class Street {
        public void printAddress() {
            System.out.println("Town is " + Town.this);
            System.out.println("PostCode is " + postCode);
            System.out.println("Street is " + this);
        }
    }

    public void createStreet() {
        Street street = new Street();
        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.

3. Локальный класс

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

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

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

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

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

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

Пример 2. Локальный класс

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();
    }
}

Пример 3. Локальный класс объявленный в статическом методе

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

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();
    }
}

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

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

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

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

Пример 4. Анонимный класс

public class Potato {
    public void peel() {
        System.out.println("Peeling potato.");
    }
}

public class Food {
    Potato p = new Potato() {
        public void fry() {
            System.out.println("Frying potato anonymously.");
        }

        public void peel() {
            System.out.println("Peeling potato anonymously.");
        }
    };

    public void prepare() {
        p.peel();
        //  p.fry(); //compilation failure
    }
}

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

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

Пример 5. Анонимный класс расширяющий интерфейс

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();
    }
}

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

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

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

Пример 6. Статический вложенный класс

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

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

    public static void main(String[] args) {
        Town4.Street street = new Town4.Street(); // both class names
        street.go();
        District district = new District(); // access the enclosed class
        district.go();
    }
}


0 comments
Leave your comment: