Try English version of Quizful



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

Список полученных сертификатов находится на странице Вашего профиля. Сертификаты можно распечатать или разместить на Вашем сайте.

Лента обновлений
ссылка 18:56:36
Комментарий от romashishka:
3 уровня требований:
1. Бизнес-требования - то, что ...
ссылка 16:01:49
Комментарий от thesky:
Часть вопросов может иметь разные ответы, все же зависит ...
ссылка 13:29:04
Комментарий от LazyFox:
+
ссылка 11:48:43
Комментарий от qwerty25:
тот же вопрос интересует
ссылка 10:39:09
Комментарий от ShevcenkoArtem:
Неточная формулировка. Конкретно фигуры рисуем на...
Статистика

Тестов: 152, вопросов: 8526. Пройдено: 353564 / 1704937.

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

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

На лицо ужасные, добрые внутри
(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

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

Голосов: 164  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