Ярлыки

.htaccess (4) тестирование (8) шаблоны проектирования (3) css (5) Debian (6) docker (2) Doctrine2 (6) Git (6) html (4) java (6) javascript (13) jquery (11) LFS (3) linux (23) mac os (4) mod_rewrite (2) MSSQL (4) MySQL (18) ORM Doctrine (17) patterns (3) PDO (3) perl (7) PHP (64) PHPUnit (8) Python (15) SEO (2) Silex (1) SimpleXML (1) SQL (14) ssh (4) Ubuntu (24) Yii1 (1) Zend Framework (19) ZendFramework2 (8)

среда, 9 марта 2011 г.

Java. Вложенные классы.

Что такое вложенный класс?

Судя по названию, вложенный класс в языке Java является классом, объявленным внутри другого класса. Вот простой пример:
public class EnclosingClass {
    ...
    public class NestedClass {
    ...
    }
}
Вы можете определить вложенный класс со спецификаторами доступа public, private или protected. Можно также определить вложенный класс с ключевыми словами final (для предотвращения его изменения), abstract (означает, что для него нельзя создать экземпляр) или static.

Любой вложенный класс имеет доступ ко всем членам окружающего его класса, даже если они объявлены со спецификатором доступа private.

Определение вложенных классов


Вложенный класс определяется также как и не вложенный класс, но делается это внутри другого класса. Для некоторого вымышленного примера давайте определим класс Wallet (кошелек) внутри класса Adult. Хотя в реальной жизни вы могли бы иметь объект Wallet отдельно от Adult, это не было бы так полезно; вполне логично, чтобы каждый Adult имел Wallet (или, по крайней мере, что-нибудь для хранения денег, а MoneyContainer звучит немного странно). Также имеет смысл, чтобы Wallet не существовал в Person, поскольку Baby не имеют его, а если бы он был в Person, его наследовали бы все подклассы Person.

Наш класс Wallet будет очень простым, поскольку служит только для демонстрации определения вложенного класса:

protected class Wallet {
protected ArrayList bills = new ArrayList();

    protected void addBill(int aBill) {
        bills.add(new Integer(aBill));
    }

    protected int getMoneyTotal() {
        int total = 0;
        for (Iterator i = bills.iterator(); i.hasNext(); ) {
             Integer wrappedBill = (Integer) i.next();
             int bill = wrappedBill.intValue();
             total += bill;
        }
        return total;
    }
}

Мы определим этот класс внутри класса Adult следующим образом:
public class Adult extends Person {
  protected Wallet wallet = new Wallet();
    public Adult() {
  }
  public void talk() {
    System.out.println("Spoke.");
  }
  public void acceptMoney(int aBill) {
    this.wallet.addBill(aBill);
  }
  public int moneyTotal() {
    return this.wallet.getMoneyTotal();
  }
  protected class Wallet {
  ...
  }
}

После определения нашего вложенного класса и нового метода acceptMoney() мы можем использовать их следующим образом:
Adult anAdult = new Adult();
anAdult.acceptMoney(5);
System.out.println("I have this much money: " + anAdult.moneyTotal());

Упрощенная обработка событий

Язык Java поддерживает подход к обработке событий с использованием соответствующих классов, что позволяет создавать и обрабатывать ваши собственные события. Но обработка событий может быть намного проще. Все, что вам действительно нужно, - это некоторая логика для генерирования "события" (которое на самом деле вовсе не обязательно должно быть классом event) и некоторая логика для прослушивания этого события и соответствующего реагирования. Например, предположим, что во время любого перемещения Person наша система генерирует (или инициирует) MoveEvent, которое мы можем выбрать для обработки, а можем и не выбрать. Это потребует нескольких изменений в нашей системе. Мы должны:

  • Создать класс "приложение" для запуска нашей системы и демонстрации использования анонимного внутреннего класса.
  • Создать интерфейс MotionListener, который наше приложение может реализовать, и затем обработать событие в прослушивателе.
  • Добавить список (List) прослушивателей в Adult.
  • Добавить метод addMotionListener() к Adult для регистрации прослушивателя.
  • Добавить метод fireMoveEvent() к Adult, для того чтобы он мог сказать прослушивателям, когда обрабатывать событие.
  • Добавить код в наше приложение для создания объекта Adult и регистрации его в качестве обработчика.

Это все не сложно. Вот класс Adult с новой функциональностью:
public class Adult extends Person {
  protected Wallet wallet = new Wallet();
  protected ArrayList listeners = new ArrayList();
  public Adult() {}
  public void move() {
    super.move(); fireMoveEvent();
  }
  ...
  public void addMotionListener(MotionListener aListener) {
    listeners.add(aListener);
  }
  protected void fireMoveEvent() {
    Iterator iterator = listeners.iterator();
    while(iterator.hasNext()) {
      MotionListener listener = (MotionListener) iterator.next();
      listener.handleMove(this);
    }
  }
  protected class Wallet {
  ...
  }
}
Обратите внимание на то, что мы сейчас переопределили move(). Сначала вызываем move() класса Person, затем вызываем fireMoveEvent() для того, чтобы указать прослушивателям среагировать на событие. Мы также добавили addMotionListener(), который добавляет MotionListener к списку прослушивателей. Вот как выглядит MotionListener:
public interface MotionListener {
 public void handleMove(Adult eventSource);
}
Все, что осталось сделать - создать наш класс приложения:
public class CommunityApplication implements MotionListener {
  public void handleMove(Adult eventSource) {
    System.out.println("This Adult moved: \n" + eventSource.toString());
  }
  public static void main(String[] args) {
    CommunityApplication application = new CommunityApplication();
    Adult anAdult = new Adult();
    anAdult.addMotionListener(application);
    anAdult.move();
  }
}
Этот класс реализует интерфейс MotionListener. Это означает, что он реализует handleMove(). Здесь мы просто выводим сообщение для демонстрации генерирования события.

Анонимные внутренние классы

Анонимные внутренние классы позволяют вам определить неименованный класс тут же, по ходу программы для обеспечения некоторого контекстно-зависимого поведения. Это обычный подход для обработчиков событий в пользовательских интерфейсах, рассмотрение которого выходит за рамки данного руководства. Но мы можем использовать анонимный внутренний класс даже в нашем упрощенном примере обработки событий.

Вы можете преобразовать приведенный на предыдущей странице пример для использования анонимного внутреннего класса, изменив вызов addMotionListener() в CommunityApplication.main() следующим образом:
anAdult.addMotionListener(new MotionListener() {
   public void handleMove(Adult eventSource) {
      System.out.println("This Adult moved: \n" + eventSource.toString());
   }
});
Вместо реализации классом CommunityApplication интерфейса MotionListener мы объявили неименованный (и, следовательно, анонимный) внутренний класс типа MotionListener и определили реализацию метода handleMove(). Тот факт, что MotionListener является интерфейсом, а не классом, не имеет значения. И то, и другое допустимо.

Этот код делает точно то же самое, что и предыдущая версия, но использует более традиционный и ожидаемый подход. Вы почти всегда будете встречать обработчики событий, реализованные с анонимными внутренними классами.

Использование вложенных классов

Вложенные классы могут быть очень полезными. Но они также могут вызывать головную боль.

Используйте вложенный класс в тех случаях, когда не имеет большого смысла определять класс вне окружающего класса. В нашем примере мы могли бы безболезненно определить класс Wallet вне класса Adult. Но представьте себе что-то подобное классу Personality (личность). Вы когда-нибудь видели его вне экземпляра Person? Нет, поэтому есть веские причины определить его в виде вложенного класса. Хорошим практическим правилом является следующее: вы должны определять класс не вложенным, пока не станет очевидным, что он должен быть вложенным. В этом случае выполните рефакторинг и вложите его в другой класс.

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

Комментариев нет:

Отправить комментарий