В этом руководстве вы познакомитесь с концепцией внутренних классов в Java — тех классов, область действия и определение которых включены в другой класс. Вы также узнаете об анонимных внутренних классах, которые довольно часто используются при разработке с Android SDK.
Приложения Android написаны на Java, объектно-ориентированном языке программирования. В этом уроке вы узнаете о внутренних классах, когда и зачем их использовать и как они работают. Вы также узнаете, как динамически создавать новые объекты, используя анонимные внутренние классы.
Что вам нужно
Технически, вам не нужны никакие инструменты для завершения этого урока, но вам, безусловно, они понадобятся для разработки приложений для Android.
Для разработки приложений Android (или любых других приложений Java) вам необходима среда разработки для написания и создания приложений. Eclipse — это очень популярная среда разработки (IDE) для Java и предпочтительная IDE для разработки под Android. Он свободно доступен для операционных систем Windows, Mac и Linux.
Полные инструкции по установке Eclipse (включая поддерживаемые версии) и Android SDK см. На веб-сайте разработчиков Android .
Что такое внутренний класс?
Большинство классов в Java являются классами верхнего уровня. Эти классы и объекты, которые они определяют, являются автономными. Вы также можете создавать вложенные классы, чтобы четко инкапсулировать и определять подчиненные объекты, которые имеют значение только в контексте внешнего класса. Вложенные классы называются внутренними классами.
Внутренние классы могут иметь все возможности обычного класса, но их область действия ограничена. Внутренние классы имеют еще одно преимущество: они имеют полный доступ к классу, в который они вложены, — эта функция делает внутренние классы идеальными для реализации функций адаптера, таких как итераторы.
Вот пример класса верхнего уровня с двумя внутренними классами:
public class User {// Поля пользователя, включая переменные типа LoginInfo и UserPreferences // Класс пользовательских методов LoginInfo {// Поля информации для входа // Методы входа / выхода // Доступ к полям пользователя / методам} Предпочтения класса {// User предпочтительные поля // Получить / установить предпочтительные методы // Сбросить предпочтительный метод // Доступ к пользовательским полям / методам}}
В этом примере класс User имеет два внутренних класса: LoginInfo и Preferences. Хотя все пользовательские данные и функциональные возможности могут быть определены в классе User, использование внутренних классов для разделения функциональных возможностей может облегчить чтение и обслуживание кода. Внутренние классы LoginInfo и Preferences также имеют доступ к защищенным / закрытым полям и методам, доступным в классе User, которые они иначе не могли бы иметь из-за безопасности, если бы они были определены как самостоятельные классы.
Однако важно помнить, что внутренние классы действительно существуют только для того, чтобы помочь разработчику организовать код; компилятор обрабатывает внутренние классы так же, как и любой другой класс, за исключением того, что внутренние классы имеют ограниченную область видимости и поэтому привязаны к классу, с которым они определены. Иными словами, вы не сможете использовать или создавать экземпляры классов LoginInfo или Preferences, кроме как с экземпляром класса User, но внутренние классы могут обращаться к любым полям или методам, доступным во внешнем классе User, по мере необходимости.
Использование статических вложенных классов
В частности, для вложенных классов используются статические вложенные классы. Статический внутренний класс определяет поведение, которое не привязано к конкретному экземпляру объекта, но применяется ко всем экземплярам. Например, мы могли бы добавить третий вложенный класс, на этот раз статический, в класс User для управления функциональностью, связанной с сервером:
открытый класс пользователя { // Пользовательские поля, включая переменные типа LoginInfo и UserPreferences // Разные пользовательские методы класс LoginInfo {} открытый статический класс ServerInfo {} { // Информация сервера относится ко всем экземплярам пользователя } }
Поскольку этот статический вложенный класс общедоступен, его экземпляр можно создать с помощью следующего нового оператора:
User.ServerInfo sInfo = new User.ServerInfo ();
Внешний класс не должен быть создан для выполнения этого экземпляра, таким образом, использование имени класса. Таким образом, статический вложенный класс, в отличие от нестатического вложенного класса (он же внутренний класс), не имеет доступа к членам внешнего класса — они могут даже не быть созданы.
Сила анонимных внутренних классов
Android использует анонимные внутренние классы с большим эффектом. Анонимные внутренние классы — это, по сути, сокращение от разработчика, позволяющее разработчику создавать, определять и использовать пользовательский объект в одной «строке». Возможно, вы видели примеры использования анонимного внутреннего класса в примере кода и даже не осознавали этого.
Чтобы создать анонимный внутренний класс, вы указываете только правую часть определения. Начните с нового ключевого слова, за которым следует класс или интерфейс, который вы хотите расширить или реализовать, а затем определение класса. Это создаст класс и вернет его как значение, которое вы затем сможете использовать для вызова метода.
Когда мы используем анонимный внутренний класс, созданному объекту не присваивается имя (таким образом, термин анонимный). Побочным эффектом, конечно, является то, что объект используется только один раз. Например, обычно используют анонимный внутренний класс для создания пользовательской версии объекта в качестве возвращаемого значения. Например, здесь мы расширяем класс Truck (предполагая, что он определен в другом месте и имеет поле с именем mpg и два метода start () и stop ():
Грузовик getTruck () { вернуть новый грузовик () { int mpg = 3; void start () {/ * начать реализацию * /} void stop () {/ * остановить реализацию * /} }; }
Теперь давайте посмотрим на практические примеры анонимных внутренних классов, используемых в Android.
Использование анонимного внутреннего класса для определения слушателя
Разработчики Android часто используют анонимные внутренние классы для определения специализированных слушателей, которые регистрируют обратные вызовы для определенного поведения, когда происходит событие. Например, чтобы прослушивать щелчки в элементе управления View, разработчик должен вызвать метод setOnClickListener (), который принимает один параметр: объект View.OnClickListener.
Разработчики обычно используют метод анонимного внутреннего класса для создания, определения и использования своего пользовательского View.OnClickListener следующим образом:
Button aButton = (Button) findViewById (R.id.MyButton); aButton.setOnClickListener (new View.OnClickListener () { public void onClick (View v) { // Пользователь нажал мою кнопку, сделайте что-нибудь здесь! } });
Использование анонимного внутреннего класса для запуска потока
Давайте посмотрим на другой пример. Довольно часто определяют новый класс Thread, предоставляют реализацию его метода run () и запускают этот поток, все за один раз:
новый поток() { public void run () { doWorkHere (); } }.Начало();
Использование именованного внутреннего класса
Использование анонимных внутренних классов для слушателей в Android настолько распространено, что это практически вторая натура. Тогда почему бы вам не использовать их? Давайте ответим на это гипотетическим примером.
Допустим, у вас есть экран с 100 кнопками (мы говорили гипотетически, верно?). Теперь, скажем, каждая кнопка при нажатии делает одно и то же . В этом случае мы будем просто слушать клики и вставлять текст из переданного объекта View (текст, отображаемый на кнопке, на которой была нажата кнопка):
Вот псевдокод для этого:
Button [] buttons = getAllOneHundredButtonsAsArray (); для (кнопка кнопка: кнопки) { button.setOnClickListener (new View.OnClickListener () { public void onClick (View v) { showToast (v.getText ()); } }); }
Короткий и элегантный, так что с ним не так? На каждой итерации создается новый объект OnClickListener. Поскольку все они одинаковы, нет веских причин для создания 100 из них. Вместо этого вы можете создать один именованный внутренний класс, один раз создать его экземпляр, а затем передать его в метод setOnClickListener (). Например:
класс MyActivity расширяет Activity { public void myMethod () { MyClickHandler handler = new MyClickHandler (); Button [] buttons = getAllOneHundredButtonsAsArray (); для (кнопка кнопка: кнопки) { button.setOnClickListener (обработчик); } } класс MyClickHandler реализует View.OnClickListener { public void onClick (View v) { showToast (((Кнопка) v) .getText ()); } } }
Если вы предпочитаете анонимность, вы все равно можете назначить анонимный внутренний класс переменной и использовать его следующим образом:
класс MyActivity расширяет Activity { public void myMethod () { View.OnClickListener handler = new View.OnClickListener () { public void onClick (View v) { showToast (((Кнопка) v) .getText ()); } }; Button [] buttons = getAllOneHundredButtonsAsArray (); для (кнопка кнопка: кнопки) { button.setOnClickListener (обработчик); } } }
Этот метод зависит от вас, но имейте в виду потенциальные проблемы с памятью и производительностью, которые могут возникнуть у экземпляра множества объектов.
Записка о нюансах
Это руководство предназначено для ознакомления с внутренними классами в Java. Есть стилевые соображения и нюансы при использовании внутренних классов разными и креативными способами. Более того, вы можете узнать больше о внутренних эффектах и предельных различиях в производительности, которые могут проявляться при использовании вложенных классов различными способами. Однако все это выходит за рамки данного руководства.
Краткое примечание по терминологии
Хотя мы пытались соответствовать терминологии для вложенных и внутренних классов, эта терминология не всегда согласуется с различными сетевыми и автономными ресурсами. Ниже приведен список терминов из текущей документации Sun / Oracle, который так же хорош, как любой из них для авторитетности на Java:
- Вложенный класс : класс, определенный внутри другого класса
- Статический вложенный класс : статический класс, определенный внутри другого класса
- Внутренний класс : нестатический вложенный класс, определенный внутри другого класса
- Локальный внутренний класс : класс, определенный в методе
- Anonymous Inner Class : безымянный класс, определенный в методе
Смущенный? Вы можете использовать методы java.lang.Class, называемые isLocalClass () и isAnonymous (), в экземплярах классов для определения пары этих свойств. Запись в блоге Oracle также пытается немного прояснить ситуацию с помощью хорошей диаграммы Венна.
Завершение
Язык программирования Java поддерживает вложенные классы, что дает разработчику большую гибкость в определении объектов. Внутренние классы могут использоваться для организации функциональных возможностей класса или для определения специализированных поведений, которые в противном случае требовали бы от разработчика предоставления данных и функциональных возможностей класса, которые действительно не должны быть представлены. Статические внутренние классы могут использоваться для определения полей и функций, которые применяются ко всем экземплярам класса. Наконец, анонимные внутренние классы предоставляют полезное сокращение для разработчиков, которые хотят одновременно создавать, определять и использовать пользовательский объект.