Try English version of Quizful



Раздаем бесплатные Q! подробности в группе Quizful.Alpha-test
Партнеры
Рекрутерам: Прескрининг кандидатов about
Топ контрибуторов
loading
loading
Знаете ли Вы, что

После прохождения теста можно комментировать вопросы теста, а Ваши комментарии увидят модераторы теста и пользователи, которым когда-либо эти вопросы попадались.

Лента обновлений
ссылка 18:33:49
Комментарий от RomaOlya:
Идиома !=Стандарт!
ссылка 16:49:39
Комментарий от merediq:
где тут дизлайк поставить за рекламу?
ссылка 15:03:31
Комментарий от sertok:
Исправьте ответ на вопрос или сам вопрос(synhronyzed с ма...
ссылка 10:42:29
Добавлен вопрос в тест JavaScript - Средний уровень
ссылка 10:34:53
Комментарий от fedor1003:
Хороший тест, правда набрал 18/20
Статистика

Тестов: 152, вопросов: 8545. Пройдено: 383809 / 1861476.

Внутренние классы в Java (inner classes java)

head tail Статья
категория
Java
дата15.05.2009
авторc0nst
голосов171

На лицо ужасные, добрые внутри
(c) Бриллиантовая рука


Рекомендуемые ссылки:

Сначала немного о терминологии. В литературе по Java встречаются такие термины, как "внутренние классы" (inner classes) и "вложенные классы" (nested classes). Говорить мы будем о вложенных классах, для которых inner классы являются подмножеством. Тем не менее часто под внутренними классами подразумеваются все вложенные - вот такой вот парадокс.

Итак, вложенный класс - это класс, который объявлен внутри объявления другого класса. Например:


class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    ...
}

Вложенные классы делятся на статические (в примере выше StaticNestedClass - это именно он) и нестатические (non-static). Собственно нестатические вложенные классы имеют и другое название - внутренние классы (inner classes). Внешний класс (outer class) мы иногда будем называть еще обрамляющим классом.

Статические вложенные классы (static nested classes)

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

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

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

Давайте рассмотрим такой пример:


public class Question {
    private Type type;

    public Type getType() { return type; }
    public void setType(Type type) { this.type = type; }

    public static enum Type {
        SINGLE_CHOICE, MULIT_CHOICE, TEXT
    }
}

В вопросе (класс Question) понятие "типа вопроса" (class Type) является очевидным. Альтернатива - создать класс верхнего уровня QuestionType, - будет менее выразительной, по крайней мере в контексте класса Question.

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


    Question.Type type = question.getType();
    if (type == Question.Type.TEXT) { ... }

Вы наверное обратили внимание, что снаружи обращение к классу Type осуществляется через имя обрамляющего класса - Question.Type. Для фрагмента кода, приведенного в начале статьи, создание класса StaticNestedClass может быть осуществлено таким образом:


    OuterClass.StaticNestedClass instance = new OuterClass.StaticNestedClass();

Еще одно интересное использование статических вложенных классов - тестирование приватных статических методов. Пример:


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

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

Дело в том, что после компиляции данного кода мы получим 2 файла - ClassToTest.class и ClassToTest$Test.class. При чем класс ClassToTest никакой информации о вложенном классе иметь не будет (если не вызывать методы вложенного класса, а это для тестов нам и не надо), а потому скомпилированный ClassToTest$Test.class потом можно просто удалить билд скриптом.

Внутренние классы-члены (member inner classes)

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

  • внутренние классы-члены (member inner classes);
  • локальные классы (local classes);
  • анонимные классы (anonymous classes).

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


public class Users {
    ...
    public class Query {
        private Query() { ... }
        public void setLogin(String login) { ... }
        public void setCreationDate(Date date) { ... }
        public List<User> list() { ... }
        public User single() { ... }
    }

    public Query createQuery() { return new Query(); }
}

В этом примере мы имеем внутренний класс Query, который предназначен для поиска пользователей по заданным параметрам. Класс Query может инкапсулировать в себе, например, работу с базой данных. При этом он имеет доступ к состоянию класса Users. Пример клиентского кода:


    Users.Query query = users.createQuery();
    query.setCreationDate(date);
    List<User> users = query.list();

Если бы конструктор класса Query был объявлен как public, создать экземпляр класса Query снаружи можно было бы только через инстанс обрамляющего класса:


    Users users = new Users();
    Users.Query query = users.new Query();

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

Еще одна важная особенность - интерфейсы в Java не могут быть инстанциированы, соответственно объявить нестатический внутренний интерфейс нельзя, так как элементарно не будет объекта для ассоциации с экземпляром внешнего класса. Объявление вида:


public class OuterClass {
    public interface ImNonStaticInterface { ... }
}

на самом деле будет интерпретироваться так:


public class OuterClass {
    public static interface ImNonStaticInterface { ... }
}

то есть неявно будет добавлен модификатор static.

Как было сказано выше, member inner class имеет доступ ко всем полям и методам обрамляющего класса. Давайте рассмотрим такой фрагмент кода:


public class OuterClass {
    public void method() { ... }

    public class InnerClass {
        public void method() { ... }
        
        public void anotherMethod() {
            method();
        }
    }
}

Вызов method() из anotherMethod обратится к методу класса InnerClass. Для обращения к методу обрамляющего класса необходимо использовать такую конструкцию - OuterClass.this.method().

Локальные классы (local inner classes)

Локальные классы (local classes) определяются в блоке Java кода. На практике чаще всего объявление происходит в методе некоторого другого класса. Хотя объявлять локальный класс можно внутри статических и нестатических блоков инициализации.

Пример использования локального класса:


public class Handler {
    public void handle(String requestPath) {
        class Path {
            List<String> parts = new ArrayList<String>();
            String path = "/";
            Path(String path) {
                if (path == null) return;
                this.path = path;
                for (String s : path.split("/"))
                    if (s.trim().length() > 0) this.parts.add(s);
            }
            int size() { return parts.size(); }
            String get(int i) { return i > this.parts.size() - 1 ? null : this.parts.get(i); }
            boolean startsWith(String s) { return path.startsWith(s); }
        }

        Path path = new Path(requestPath);

        if (path.startsWith("/page")) {
            String pageId = path.get(1);
            ...
        }
        if (path.startsWith("/post")) {
            String categoryId = path.get(1);
            String postId = path.get(2);
            ...
        }
        ...
    }
}

Данный код с некоторыми изменениями взят из реального проекта и используется для обработки get запросов к веб-серверу. Он вводит новую абстракцию, с которой удобно работать в пределах метода и которая не нужна за его пределами.

Как и member классы, локальные классы ассоциируются с экземпляром обрамляющего класса и имеют доступ к его полям и методам. Кроме этого, локальный класс может обращаться к локальным переменным и параметрам метода, если они объявлены с модификатором final.

У локальных классов есть множество ограничений:

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

Кстати, интерфейсы тоже нельзя объявлять локально по тем же причинам, по каким их нельзя объявлять внутренними.

Анонимные классы (anonymous inner classes)

Анонимные классы являются важным подспорьем в повседневной жизни Java-программистов. Анонимный класс (anonymous class) - это локальный класс без имени.

Классический пример анонимного класса:


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

На основании анонимного класса создается поток и запускается с помощью метода start класса Thread. Синтаксис создания анонимного класса базируется на использовании оператора new с именем класса (интерфейса) и телом новосозданного анонимного класса.

Основное ограничение при использовании анонимных классов - это невозможность описания конструктора, так как класс не имеет имени. Аргументы, указанные в скобках, автоматически используются для вызова конструктора базового класса с теми же параметрами. Вот пример:


class Clazz {
    Clazz(int param) { }

    public static void main(String[] args) {
        new Clazz(1) { }; // правильное создание анонимного класса
        new Clazz() { }; // неправильное создание анонимного класса
    }
}

Так как анонимный класс является локальным классом, он имеет все те же ограничения, что и локальный класс.

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

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

Ну вот наверное и все. Остается только сказать, что использование с умом возможностей, которые предоставляют вложенные классы, сделают ваш код чище, красивее и понятнее.


---------

Константин c0nst Чапюк, 23.04.2009

Если Вам понравилась статья, проголосуйте за нее

Голосов: 171  loading...
admin   patisonka   stalker   kukulya   breusov   kraka   doctorlife   alexis112   oleger   AlexF   izen   kuzvld   matias   lestat   faxitos   fedind   generator   DodgeWP   onuchinart   kosolapiy   FAA   bu_ma_ga   ShootDP   alon4ik   helsinki   ldkfa   abihle   scythiang   grim   helpa   wisd   dreamnewq   long   pkunilov   JinSem   Nastiona   Alexander_1   Saimon   Citsym   Taky   safonik   Lord   tempatel   dlevshunov   prostodenis   korvindest   akhmetoff   Mazhor1   milkomil   Evmorov   alex1395   Skrakan   krieger_   oleg_pr   KateAlex   ViRuS   poncheg   krab2424   serdyk_alex   alexrav   strange_bear   R2D2   michalerain   Elen_KL   Evgenija   marmota   nasena_08   HelgaBogu   Authorized   Kadavrius   rds29   welcometo   Riddler   Karamba   St1G   nick2003   SerXio_82   ama3onka   Maxanzh   KirAA   dgroup   sergey_85   Romantic   nixdorf2003   ShevcenkoArtem   Asdfgh   epfl   activecs   SichikUA   VeLKerr   lonely   heroin   Sebastian   Ericsson1   D_S__   moreking   Delivior   Figishe   verysamf   nick_kryloff   DanikG   z84   r_leha   Darwind_   KseniaSevridova   alexgiant   TeaWitch   TheEvilsscream   piggygoose   ralike_13   Ruzveld   Giggs13   Pasha_Pasha   disoku   arsenius75   Ggekko   pristroistvo_ek   whak   izakacman   nklskr   Testikusik11   edivaliev   desay   KZ_Jumper   SkunS   LuxCore   creed   boohaky   Richardiii   dtsuran   maks1m   SamTan   jacksolovey   ronaldo173   QuizfulClient   gggbrown   lyapizz   ma1m2   forest_dm   scrobot   arisenUA   sikmak   yagoo   pyshankov   Nihlus   iLynx   Buba_Kastorskiy   Flubby   SKS   Swites   Negling   vvm64   jcd3   chipuk   RootS   Sulfur   sadpanda   samvgorode   planetscape   pekky   eutohius   Andrikkk   AlexShevchenko   captian   Lich87   verrchu   master_musi   Sawjkee   egorw   Drager27   jeffhardy2209