Лекция 07. Вложенные и внутренние классы

Классификация

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

Обычные классы в Java относятся к классам верхнего уровня.

Помимо них в программах могут существовать следующие разновидности вложенных и внутренних классов:

  1. Статические вложенные классы (static nested classes).
  2. Внутренние классы (inner classes).
  3. Локальные классы (local classes).
  4. Анонимные классы (anonumous classes).

Для чего используют вложенные и внутренние классы:

  • Логическая группировка сущностей (все нужное - в одном месте)
  • Усиление инкапсуляции (‘’богатый внутренний мир’‘)
  • Обеспечение доступа к private-членам со стороны некоторых классов (их делают внутренними)
  • Улучшение читаемости программы (все необходимое ‘’под рукой’‘)

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

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

Статические вложенные классы (СВК) не имеют доступа к нестатическим полям и методам обрамляющего класса. Доступ к нестатическим полям и методам может осуществляться только через ссылку на экземпляр обрамляющего класса.

СВК имеют доступ к любым статическим методам внешнего класса, в том числе и к приватным.

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

В роли СВК могут выступать интерфейсы

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

Статический вложенный класс (СВК) определяется внутри другого класса:

class A {
   static class B { }
   static interface C {}
}

Такие классы (B или C) никак не связаны с экземплярами класса A.

СВК имеет доступ ко всем static-членам внешнего класса, включая закрытые.

Внешний класс тоже имеет доступ ко всем static - членам СВК. Вот так можно создавать экземпляры вложенных статических классов:

class OuterClass {
    static class StaticNestedClass {
        ...
    }
}
OuterClass.StaticNestedClass instance =
             new OuterClass.StaticNestedClass();

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

Интересное использование статических вложенных классов -

тестирование приватных статических методов:

public class ClassToTest {
    private static void internalMethod() { ... }

    public static class Test {
        public void testMethod() { ... }
    }
}

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

Работа с внутренними классами похожа на работу со статическими вложенными классами, но происходит внутри объектов.

class A {
    class B {
        ...
    }
    public B get() {
        return new B();
    }
}

A.B b=a.get();

Данный класс (B) не может иметь статических элементов, а также перечислений.

  • Класс B известен только внутри класса A
  • Класс B имеет доступ к членам класса A, но класс А не имеет доступа к членам, объявленным внутри B
  • После компиляции: A.class и A$B.class

Каждый экземпляр внутреннего класса связан с экземпляром внешнего класса.

Нельзя создать экземпляр внутреннего класса, не создав экземпляр внешнего. Внутренние классы часто используются для предоставления услуг внешним классам.

class A {
   class B {
      private int data;
      public B(int data) {
         this.data=data;
      }
      public int get() {
         return data;
      }
    }
    public B getB(int data) {
       return new B(data);
    }
}
public class Program
{
   public static void main(String[] args) {
      A a=new A();
      A.B c=a.new B(100);
      A.B b=a.getB(10);
      System.out.println(b.get());
      System.out.println(c.get());
   }
}

Внешний класс часто содержит метод (вроде getB), возвращающий ссылку на объект внутреннего класса.

Локальные классы

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

Локальные вложенные классы можно создавать прямо в коде методов:

class A {
   void method() {
      class B {
      ...
      }
      B b=new B();
      ...
   }
}

Ограничения:

  • они видны только в пределах блока, в котором объявлены;
  • они не могут быть объявлены как private, public, protected или static;
  • они не могут иметь внутри себя статических объявлений (полей, методов, классов); исключением являются константы (static final);

Локальные классы

Внутренний класс в локальном методе не может использовать локальные переменные внешнего метода до тех пор, пока переменная не будет объявлена как final

class Outer {
    public void out() {
        final int val=100;
        class Inner {
            public int get() {
                return val;
            }
        }
        Inner in=new Inner();
        System.out.println(in.get());
    }
}

Анонимные классы

Анонимный класс (anonymous class) - это локальный класс без имени.

new Thread(new Runnable() {
   public void run() {
      ...
   }
}).start();

Использование анонимных классов оправдано в случаях, когда:

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

В отличие от локальных классов, анонимные должны наследовать существующий класс или интерфейс.

Анонимные классы не могут содержать определение статических полей, методов и классов (кроме констант).

interface Employee {
    void info();
}
public class Program {
    public static void main(String[] args) {
        Employee emp=new Employee() {
            public void info() {
                System.out.println("Yes!");
            }
        };
        emp.info();
    }
}

Вопросы для самоконтроля