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

Эта статья находится на начальном уровне проработки, в одной из её версий выборочно используется текст из источника, распространяемого под свободной лицензией
Материал из энциклопедии Руниверсалис

Внутренний, или вложенный класс (англ. inner class) — в объектно-ориентированном программировании класс, целиком определённый внутри другого класса.

Вложенные классы поддерживаются в языке программирования Java, начиная с версии 1.1, С# и других языках на платформе .NET, а также в языке программирования D и в C++.

Обзор

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

Внутренние классы в языке Java

В Java существуют 4 типа внутренних (inner) классов:

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

Экземпляр внутреннего класса может существовать только тогда, когда существует конкретный экземпляр внешнего класса. Такая логическая связь обусловливает синтаксис создания объектов: сначала создаётся объект внешнего класса, позднее на его основе создаётся объект внутреннего класса.

Внутренние нестатические классы описываются внутри основного внешнего класса. Экземпляры таких классов имеют доступ к public, protected, default и private полям внешнего класса. А также статическим и нестатическим методам внешнего экземпляра с любыми модификаторами доступа. За счёт того, что экземпляры внутреннего класса всегда логически привязаны к экземплярам окружающего класса, они не могут содержать (хотя могут наследовать от предка) определение статических полей, методов и классов (кроме констант).[1]

Пример объявления внутреннего класса:

class OuterClass {
    
    private int outerField;
    
    class InnerClass {
        int getOuterField() {
            return OuterClass.this.outerField; // эта строчка кода демонстрирует концепцию замыкания.
        }
    }
    
}

Создание описанного класса можно описать следующим блоком кода: OuterClass.InnerClass inner = new OuterClass().new InnerClass();

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

Декларируются внутри основного класса и обозначаются ключевым словом static. Объекты таких классов не имеют доступа к членам внешнего класса за исключением статических. Это обусловлено тем, что для создания такого класса не используется конкретный объект внешнего класса и в момент исполнения кода внутреннего класса, объекта внешнего может вовсе не быть. Экземпляры статических вложенных классов могут содержать статические поля, методы и классы, в отличие от других типов внутренних классов.

Пример объявления вложенного статического класса:

class OuterClass {
    
    private int outerField;
    static int staticOuterField;
    
    static class StaticInnerClass {
        int getOuterField() {
            return OuterClass.this.outerField; // эта строчка кода приведёт к ошибке компиляции.
        }
        int getStaticOuterField() {
            return OuterClass.staticOuterField; // эта строчка кода корректна.
        }
    }
    
}

Создание описанного статического вложенного класса можно описать следующим блоком кода: OuterClass.StaticInnerClass staticInner = new OuterClass.StaticInnerClass();

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

Декларируются внутри методов основного класса. Могут быть использованы только внутри этих методов. Имеют доступ к членам внешнего класса. Имеют доступ как к локальным переменным, так и к параметрам метода при одном условии - переменные и параметры, используемые локальным классом, должны быть задекларированы final. Не могут содержать определение (но могут наследовать) статических полей, методов и классов (кроме констант).[2]

Пример:

class OuterClass {
    
    private int outerField;
    
    void methodWithLocalClass(final int finalParameter) {
        int notFinalVar = 0;
        notFinalVar++;
        
        class InnerLocalClass {
            void getOuterField() {
                int a = notFinalVar; // эта строчка кода приведёт к ошибке компиляции. no-final переменные вне контекста недоступны.
                int b = OuterClass.this.outerField; // эта строчка кода демонстрирует обращение члену обрамляющего класса.
            }
            
            int getParameter() {
                return finalParameter; // эта строчка кода демонстрирует обращение к final переменной вне контекста.
            }
        }
    }
}

Создание описанного локального класса возможно только внутри самого метода строго ниже кода объявления самого класса. Пример кода создания: InnerLocalClass innerLocal = new InnerLocalClass();

Анонимные (безымянные) классы

Декларируются внутри методов основного класса. Могут быть использованы только внутри этих методов. В отличие от локальных классов, анонимные классы не имеют названия. Главное требование к анонимному классу - он должен наследовать существующий класс или реализовывать существующий интерфейс. Не могут содержать определение (но могут наследовать) статических полей, методов и классов (кроме констант). Пример:

class OuterClass {
    
    /** 
    *   При определении анонимного класса применен полиморфизм — переменная listener 
    *   содержит экземпляр анонимного класса, реализующего существующий 
    *   интерфейс ActionListener.
    **/
    void methodWithAnonymousClass(final int interval) {
        ActionListener listener = new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                System.out.println("Эта строка выводится на экран каждые " + interval + " секунд.");
            }  
        };
    
        Timer t = new Timer(interval, listener); // объект анонимного класса использован внутри метода.
        t.start();
    }
    
}

Внутренние классы в других языках программирования

В PHP 7 есть механизм описания анонимных классов, однако, в отличие от Java, анонимные классы не обязаны наследовать существующий класс или реализовывать существующий интерфейс, что достигается благодаря динамической природе языка. Пример:

// Pre PHP 7 code
class Logger
{
    public function log($msg)
    {
        echo $msg;
    }
}

$util->setLogger(new Logger());

// PHP 7+ code
$util->setLogger(new class {
    public function log($msg)
    {
        echo $msg;
    }
});

См. также

Литература

Cay S. Horstmann and Gary Cornell, Core Java, eighth edition (Volume I). Prentice Hall, 2003. ISBN 978-0132354769 (ссылка на страницу книги)

Примечания

  1. Oracle. The Java™ Tutorials. Inner classes. Oracle Documentation. Дата обращения: 12 апреля 2016. Архивировано 22 марта 2016 года.
  2. Local Classes (The Java™ Tutorials > Learning the Java Language > Classes and Objects). docs.oracle.com. Дата обращения: 12 апреля 2016. Архивировано 31 марта 2016 года.

Ссылки