Почему мы используем внутренние классы?

Я хочу спросить вас, зачем нужны внутренние классы и почему мы их используем ?
Я знаю, как использовать внутренние классы, но я не знаю, почему..

7 ответов


некоторые внутренние классы открытые (например,Map.Entry в Java), но это однозначно исключение, а не норма.

внутренние классы-это, в основном, деталь реализации.

например, Swing широко использует внутренние классы для прослушивателей событий. Без них вы в конечном итоге загрязняете глобальное пространство имен кучей классов, которые вам в противном случае не нужно видеть (что может затруднить определение их цели).

по сути внутренний классы-это форма области. Доступ к пакету скрывает классы вне пакета. Частные внутренние классы скрывают этот класс от внешнего класса.

внутренние классы в Java также заменяют отсутствие указателей функций или делегатов методов (которые находятся в C#) или замыканий. Они являются средством передачи функции другой функции. Например, в Executor класс у вас есть:

void execute(Runnable r);

таким образом, вы можете передать метод. В C / C++ это может быть достигнуто с:

void execute(void (*run)());

быть указателем на функцию.


большую часть времени я использую внутренние классы внутренние классы потому, что находятся ближе всего к понятию закрытие доступен на других языках. Это позволяет создавать и работать с объектом внутренней вложенной области, который имеет доступ к переменным внешней области. Это часто полезно при создании обратных вызовов (например, определение различных ListenerS в Swing) среди прочего.


анонимные внутренние классы в Java-это способ использования шаблона адаптер.

interface Bar
{
  public void bar();
}

class Foo
{
  public void foo()
  {
    // do something relevant
  }

  // it happens that foo() defines the same contract (or a compatible one) as
  // Bar.bar(); with an anonymous inner class we can adapt Foo to the Bar
  // interface
  public Bar asBar()
  {
    // return an instance of an anonymous inner class that implements
    // the Bar inteface
    return new Bar()
    {
      public void bar()
      {
        // from an inner class, we can access the enclosing class methods
        // as the "this pointers" are "linked"
        foo();
      }
    };
  }
}

в Java, убедитесь, что вы понимаете разница между внутренними классами и вложенным классом:

внутренний класс связан с экземпляр его заключительного класса и имеет прямой доступ к объекту методы и поля

C# не имеет внутренних классов в смысле Java, только вложенные классы.

Смотрите также Пример Внутреннего Класса.


с Википедия может помочь вам понять, зачем нужен внутренний класс:

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

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

У нас есть автомобиль класса верхнего уровня. Экземпляры класса Car состоят из четырех экземпляров класса Wheel. Эта конкретная реализация колеса специфична для автомобиля, поэтому код не моделирует общее понятие колеса, которое было бы лучше представлено как класс верхнего уровня. Поэтому он семантически связан с классом автомобиля, а код колеса каким-то образом связан с его внешним классом.

внутренние классы предоставляют нам механизм для точной модели этой связи. Мы говорим, что наш класс колес-автомобиль.Колесо, машина была на топ-уровне класса и играла внутреннего класса.

внутренние классы поэтому учитывают объектную ориентацию определенных частей программы, которые в противном случае не были бы инкапсулированы в класс.


Я использую их для охвата, например, если у меня есть класс ebook и у меня есть ebookPrice, я заключаю ebookPrice между классом ebook, поскольку он связан с ним и может использоваться только (по крайней мере концептуально) внутри него.

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

(просто мое мнение).


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

для хорошего охвата этого видения, пожалуйста, обратитесь к " сколько концепций для модулей нам нужно?" в блоге object teams. Смотрите также работу Гилада Брачи на блог...


объектно-ориентированное преимущество

давайте посмотрим на член класса. Поскольку его экземпляр является членом родительского экземпляра, он имеет доступ к каждому члену и методу в Родительском экземпляре. На на первый взгляд, это может показаться не так много; у нас уже есть такой доступ изнутри метода в родительском классе. Однако класс-член позволяет нам извлечь логику из родителя и объективировать ее. Например, класс tree может иметь метод и множество вспомогательных методов, выполняющих поиск или прогулку по дереву. С объектно-ориентированной точки зрения дерево является деревом, а не алгоритмом поиска. Однако для выполнения поиска требуется глубокое знание структур данных дерева.

внутренний класс позволяет нам удалить эту логику и поместить ее в свой собственный класс. Итак, с объектно-ориентированной точки зрения, мы взяли функциональность там, где она не принадлежит, и поместили ее в свой собственный класс. Благодаря использованию внутреннего класса мы успешно отделили алгоритм поиска от дерева. Теперь, чтобы изменить алгоритм поиска, мы можем просто поменять местами в новом классе. Я мог бы продолжить, но это открывает наш код для многих преимуществ, предоставляемых объектно-ориентированные методы.

организационное преимущество

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

package1
   class 1
      class 2
      ...
      class n
...
package n

с внутренними классами мы можем сделать следующее:

package 1
   class 1
   class 2
      class 1
      class 2
      ...
      class n

используется тщательно, внутренние классы могут обеспечить структурную иерархию, которая более естественно соответствует вашим классам.

преимущество обратного вызова

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

большинство Java GUIs имеют какой-то компонент, который инициирует вызов метода actionPerformed (). К сожалению, большинство разработчиков просто имеют свое главное окно реализации ActionListener. В результате все компоненты используют один и тот же метод actionPerformed (). Чтобы выяснить, какой компонент выполнил действие, обычно существует гигантский, уродливый переключатель в методе actionPerformed ().

вот пример монолитного реализация:

public class SomeGUI extends JFrame implements ActionListener {

    protected JButton button1;
    protected JButton button2;
    //...
    protected JButton buttonN;

    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == button1) {
        // do something
        } else if (e.getSource() == button2) {
            //... you get the picture
        }
    }
}

всякий раз, когда вы видите переключатели или большие блоки if/if else, громкие тревожные колокола должны начать звонить в вашем уме. В общем, такие конструкции являются плохим объектно-ориентированным дизайном, так как изменение в одном разделе кода может потребовать соответствующего изменения оператора switch. Внутренние классы-члены и анонимные классы позволяют нам уйти от переключаемого метода actionPerformed ().

вместо этого мы можем определить внутренний класс, который реализует ActionListener для каждого компонента, который мы хотим слушать. Это может привести ко многим внутренним классам. Однако мы можем избежать больших операторов switch и иметь дополнительный бонус инкапсуляции нашей логики действий. Кроме того, такой подход может повысить производительность. В коммутаторе, где есть N сравнений, мы можем ожидать N / 2 сравнений в среднем случае. Внутренние классы позволяют нам установить соответствие 1:1 между исполнителем действия и слушателем действия. В большом GUI такие оптимизации могут существенное влияние на производительность. Анонимный подход может выглядеть так:

public class SomeGUI extends JFrame {
    //  ... button member declarations ...

    protected void buildGUI() {
        button1 = new JButton();
        button2 = new JButton();
        //...
        button1.addActionListener(
                new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent e) {
                // do something
            }
        });
// .. repeat for each button
    }
}

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

public class SomeGUI extends JFrame
{
   ... button member declarations ...
   protected void buildGUI()
   {
      button1 = new JButton();
      button2 = new JButton();
      ...
      button1.addActionListener(
         new java.awt.event.ActionListener()
         {
            public void actionPerformed(java.awt.event.ActionEvent e)
            {
               // do something
            }
         }
      );
      .. repeat for each button

поскольку внутренние классы имеют доступ ко всему в Родительском, мы можем переместить любую логику, которая появилась бы в монолитной реализации actionPerformed() во внутренний класс.

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

недостатки?

как и все остальное, вы должны принять хорошее с плохим. У внутренних классов есть свои недостатки. С точки зрения обслуживания неопытные разработчики Java могут найти внутренний класс трудным для понимания. Использование внутренних классов также увеличит общее число классы в коде. Более того, с точки зрения разработки большинство инструментов Java немного не поддерживают внутренние классы. Например, я использую IBM VisualAge for Java для своего повседневного кодирования. В то время как внутренние классы будут компилироваться в VisualAge, нет внутреннего браузера классов или шаблона. Вместо этого необходимо просто ввести внутренний класс непосредственно в определение класса. К сожалению, это затрудняет просмотр внутреннего класса. Это также трудно напечатать, так как вы теряете многие из Завершение кода VisualAge помогает при вводе в определение класса или использовании внутреннего класса