В этом быстром уроке обсуждается ряд советов по работе с внутренними классами в Java. Этот урок является частью непрерывной серии учебных пособий для разработчиков, изучающих Java с целью разработки приложений для Android.
Что такое (анонимные) внутренние классы?
В Java классы могут быть вложены друг в друга для упорядоченной организации данных и функций. Анонимные внутренние классы — это, по сути, сокращение от разработчика, позволяющее разработчику создавать, определять и использовать пользовательский объект за один раз. Анонимные внутренние классы часто используются в Android для определения обработчиков для элементов управления, определения и запуска новых потоков, а также создания и возврата пользовательских объектов в методы, не загромождая код ненужными настройками.
Подробное обсуждение вложенных и внутренних классов, включая анонимные внутренние классы, см. В нашем учебном пособии под названием Изучение Java для разработки под Android: внутренние классы.
Доступ к внешним переменным с помощью последнего ключевого слова
Иногда вы хотите получить доступ к информации, доступной за пределами внутреннего класса. Рассмотрим следующий пример. У вас есть экран с двумя элементами управления: кнопка и TextView. Каждый раз, когда пользователь нажимает на элемент управления Button, элемент управления TextView обновляется с использованием текущего времени. В классе Activity, связанном с этим макетом, вы можете реализовать эту функцию следующим образом:
Button myButton = (Button) findViewById (R.id.ButtonToClick); myButton.setOnClickListener (new View.OnClickListener () { public void onClick (View v) { SimpleDateFormat formatter = new SimpleDateFormat ("ч: мм: сс а"); String strWhen = formatter.format (new Date ()); TextView myTextview = (TextView) findViewById (R.id.TextViewToShow); myTextview.setText ("Нажмите на" + strWhen); } });
Приведенный выше код будет работать и вести себя как ожидалось. Однако предположим, что вы действительно хотели объявить элемент управления TextView вне внутреннего класса и использовать его для других целей. Возможно, вы попытаетесь сделать это вместо этого:
TextView myTextview = (TextView) findViewById (R.id.TextViewToShow); Button myButton = (Button) findViewById (R.id.ButtonToClick); myButton.setOnClickListener (new View.OnClickListener () { public void onClick (View v) { SimpleDateFormat formatter = new SimpleDateFormat ("ч: мм: сс а"); String strWhen = formatter.format (new Date ()); myTextview.setText ("Нажмите на" + strWhen); } });
К сожалению, это не скомпилируется. Вместо этого вы получите ошибку компиляции «Невозможно обратиться к неконечной переменной myTextview внутри внутреннего класса, определенного в другом методе». Как следует из ошибки, вам нужно сделать финальную переменную TextView, чтобы она была доступна внутри метода onClick () внутреннего класса:
final TextView myTextview = (TextView) findViewById (R.id.TextViewToShow); Button myButton = (Button) findViewById (R.id.ButtonToClick); myButton.setOnClickListener (new View.OnClickListener () { public void onClick (View v) { SimpleDateFormat formatter = new SimpleDateFormat ("ч: мм: сс а"); String strWhen = formatter.format (new Date ()); myTextview.setText ("Нажмите на" + strWhen); } });
Этот код будет действительно скомпилирован и запущен, как и ожидалось.
Работа с конечными переменными в Java
Сделав переменную TextView финальной в предыдущем примере, вы сделали ее доступной для анонимного внутреннего класса (или любого внутреннего класса, определенного в этой области). Вот еще кое-что, что нужно знать о конечных переменных:
- Вы не можете изменить значение (r-значение) конечной переменной. Например, если вы попытаетесь присвоить переменную myTextview другому элементу управления в методе onClick () внутреннего класса, вы получите ошибку компиляции: «Конечная локальная переменная myTextview не может быть назначена, поскольку она определена в включающем типе. »
- Вы можете вызывать методы последней переменной, если у вас есть доступ. Это то, что позволяет нам вызывать метод setText () TextView. Это не меняет значение переменной myTextview, оно просто меняет то, что отображается на экране — тонкое, но важное отличие. (Причина этого в том, что ссылка на объект не может измениться, но сам объект может измениться с помощью своих методов.)
- Значение окончательной переменной не нужно знать во время компиляции, тогда как статическая переменная известна во время компиляции.
- Ключевое слово final часто соединяется со статической переменной (полем или переменной, привязанной ко всем экземплярам класса, а не к одному экземпляру), чтобы создать константу для использования в вашем приложении, например: public static final String DEBUG_TAG, используемая для Цели ведения журнала LogCat на протяжении всей вашей деятельности.
Внутренние классы и переменная «Это»
В Java вы можете использовать специальную ссылку this для ссылки на конкретный экземпляр объекта. Так что же происходит, когда у вас есть класс с внутренним классом или, в этом отношении, анонимный внутренний класс? Ну, внутренний класс может обращаться к специальному экземпляру this экземпляра окружающего класса, и у него также есть собственная ссылка this. Чтобы получить доступ к экземпляру this для внутреннего класса, просто используйте синтаксис this. Чтобы получить доступ к этому экземпляру включающего класса, вам нужно прикрепить имя включающего класса, затем точку, затем this. Например:
MyEnclosingClass.this
Взгляните на полную реализацию анонимного внутреннего класса, как обсуждалось выше, в полном контексте включающего его класса, здесь называемого ClassChaosActivity:
пакет com.androidbook.classchaos; import java.text.SimpleDateFormat; импорт java.util.Date; импорт android.app.Activity; импорт android.os.Bundle; импорт android.util.Log; импорт android.view.View; импорт android.widget.Button; import android.widget.TextView; открытый класс ClassChaosActivity extends Activity {public static final String DEBUG_TAG = "MyLoggingTag"; / ** Вызывается при первом создании действия. * / @Override public void onCreate (Bundle saveInstanceState) {super.onCreate (saveInstanceState); setContentView (R.layout.main); final TextView myTextview = (TextView) findViewById (R.id.TextViewToShow); Button myButton = (Button) findViewById (R.id.ButtonToClick); myButton.setOnClickListener (new View.OnClickListener () {public void onClick (View v) {SimpleDateFormat formatter = new SimpleDateFormat ("h: mm: ss a"); String strWhen = formatter.format (new Date ()); myTextview. setText ("Clicked at" + strWhen); Log.v (DEBUG_TAG, "имя этого класса:" + this.getClass (). getName ()); Log.v (DEBUG_TAG ", это расширяет интерфейс с именем:" + this. getClass (). getInterfaces () [0] .getName ()); Log.v (DEBUG_TAG, «имя этого класса включения:» + this.getClass (). getEnclosingClass (). getName ()); Log.v (DEBUG_TAG , "это анонимный класс?" + this.getClass (). isAnonymousClass ()); Log.v (DEBUG_TAG, "ClassChaosActivity.this имя класса:" + ClassChaosActivity.this.getClass (). getName ()); Log. v (DEBUG_TAG, "ClassChaosActivity.this имя этого суперкласса:" + ClassChaosActivity.this.getClass (). getSuperclass (). getName ()); Log.v (DEBUG_TAG, "ClassChaosActivity.this является анонимным классом?" + ClassChaosActivity.this .getClass (). isAnonymousClass ());}}); }}
Вывод журнала для нажатия кнопки происходит следующим образом:
10-24 18: 18: 53.075: VERBOSE / MyLoggingTag (751): имя этого класса: com.androidbook.classchaos.ClassChaosActivity $ 1 10-24 18: 18: 53.085: VERBOSE / MyLoggingTag (751): расширяет интерфейс с именем: android.view.View $ OnClickListener 10-24 18: 18: 53.085: VERBOSE / MyLoggingTag (751): это имя класса включения: com.androidbook.classchaos.ClassChaosActivity 10-24 18: 18: 53.095: VERBOSE / MyLoggingTag (751): это анонимный класс? правда 10-24 18: 18: 53.095: VERBOSE / MyLoggingTag (751): ClassChaosActivity.this Имя класса: com.androidbook.classchaos.ClassChaosActivity 10-24 18: 18: 53.105: VERBOSE / MyLoggingTag (751): ClassChaosActivity.this это имя суперкласса: android.app.Activity 10-24 18: 18: 53.105: VERBOSE / MyLoggingTag (751): ClassChaosActivity.this является анонимным классом? ложный
Как видите, ключевое слово this само по себе относится к «ближайшему» классу — внутреннему классу. Хотя внутренний класс является анонимным, Java присваивает ему номер ClassChaosActivity $ 1, чтобы отслеживать его. Хотя это само по себе бесполезно для разработчиков, это показывает, что анонимный внутренний класс обрабатывается внутренне как любой другой класс. Тем временем мы получаем доступ к экземпляру включающего класса, используя синтаксис ClassChaosActivity.this.
Вывод
В этом коротком уроке вы выучили несколько советов, которые помогут вам более умело использовать внутренние классы и анонимные внутренние классы. Вы узнали, что внутренние классы могут обращаться к переменным, объявленным вне их области действия, при условии, что переменные помечены как окончательные и, следовательно, неизменяемые. Вы также узнали о специальном синтаксисе, связанном с ключевым словом this, когда речь идет о доступе к внутренним данным экземпляра класса, а также к данным включающего его экземпляра класса.
Об авторах
Разработчики мобильных приложений Лорен Дарси и Шейн Кондер являются соавторами нескольких книг по разработке Android: углубленная книга по программированию под названием « Разработка беспроводных приложений для Android» и « Разработка Android-приложений Sams TeachYourself за 24 часа» . Когда они не пишут, они тратят свое время на разработку мобильного программного обеспечения в своей компании и оказание консультационных услуг. С ними можно связаться по электронной почте [email protected] , через их блог на androidbook.blogspot.com и в Twitter @androidwireless .