Объектно-ориентированное преимущество
По моему скромному мнению, наиболее важной особенностью внутреннего класса является то, что он позволяет вам превращать вещи в объекты, которые вы обычно не превращали бы в объекты. Это позволяет вашему коду быть еще более объектно-ориентированным, чем без внутренних классов.
Давайте посмотрим на класс члена. Поскольку его экземпляр является членом его родительского экземпляра, он имеет доступ к каждому члену и методу в родительском экземпляре. На первый взгляд это может показаться не таким уж большим; у нас уже есть такой доступ из метода в родительском классе. Тем не менее, класс-член позволяет нам брать логику из родителя и объективировать ее. Например, у класса дерева может быть метод и много вспомогательных методов, которые выполняют поиск или обход дерева. С объектно-ориентированной точки зрения дерево - это дерево, а не алгоритм поиска. Тем не менее, вам нужно глубокое знание структур данных дерева для выполнения поиска.
Внутренний класс позволяет нам удалить эту логику и поместить ее в свой собственный класс. Таким образом, с объектно-ориентированной точки зрения, мы взяли функциональность там, где она не принадлежит, и поместили ее в свой собственный класс. Благодаря использованию внутреннего класса, мы успешно отцепили алгоритм поиска от дерева. Теперь, чтобы изменить алгоритм поиска, мы можем просто поменять новый класс. Я мог бы продолжить, но это открывает наш код для многих преимуществ, предоставляемых объектно-ориентированными методами.
Организационное преимущество
Объектно-ориентированный дизайн - вещь не для всех, но, к счастью, внутренние классы предоставляют больше. С организационной точки зрения внутренние классы позволяют нам дополнительно организовать нашу структуру пакета посредством использования пространств имен. Вместо того, чтобы выгружать все в плоский пакет, классы могут быть далее вложены в классы. Явно без внутренних классов мы были ограничены следующей иерархической структурой:
package1
class 1
class 2
...
class n
...
package n
С внутренними классами мы можем сделать следующее:
package 1
class 1
class 2
class 1
class 2
...
class n
При тщательном использовании внутренние классы могут обеспечить структурную иерархию, которая более естественно соответствует вашим классам.
преимущество обратного вызова
Внутренние классы-члены и анонимные классы предоставляют удобный метод для определения обратных вызовов. Самый очевидный пример относится к коду GUI. Однако приложение обратного вызова может распространяться на многие домены.
В большинстве графических интерфейсов Java есть какой-то компонент, вызывающий вызов метода 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
}
}
}
Всякий раз, когда вы видите переключатели или большие, если / если еще блокирует, громкие сигналы тревоги должны начать звонить в вашем уме. В общем, такие конструкции являются плохим объектно-ориентированным проектом, поскольку изменение в одном разделе кода может потребовать соответствующего изменения в операторе switch. Внутренние классы-члены и анонимные классы позволяют нам уйти от переключенного метода actionPerformed ().
Вместо этого мы можем определить внутренний класс, который реализует ActionListener для каждого компонента, который мы хотим слушать. Это может привести ко многим внутренним классам. Однако мы можем избежать больших операторов switch и получить дополнительный бонус - инкапсулировать нашу логику действий. Более того, такой подход может улучшить производительность. В переключателе, где есть n сравнений, мы можем ожидать n / 2 сравнений в среднем случае. Внутренние классы позволяют нам установить соответствие 1: 1 между исполнителем действия и слушателем действия. В большом графическом интерфейсе такие оптимизации могут оказать существенное влияние на производительность. Анонимный подход может выглядеть так:
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-инструментов немного отстают от поддержки внутренних классов. Например, я использую VisualAge для Java от IBM для повседневного кодирования. В то время как внутренние классы будут компилироваться в VisualAge, нет внутреннего браузера класса или шаблона. Вместо этого вы должны просто ввести внутренний класс непосредственно в определение класса. К сожалению, это затрудняет просмотр внутреннего класса. Это также трудно набирать, так как вы теряете много средств для завершения кода VisualAge, когда вы вводите определение класса или используете внутренний класс