Философия Java

Зачем внутренние классы?


До этого момента Вы видели множество примеров синтаксиса и семантического описания работы внутренних классов, но еще не было ответа на вопрос "А зачем они собственно?". Зачем Sun пошел на такие значительные усилия, что бы добавить эту возможность языка?

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

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

Каждый внутренний класс может быть независимо наследован от реализации. Поэтому, внутренний класс не ограничен тем, если внешний класс уже наследовал от реализации.

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

Что бы увидеть это более детально, представьте себе ситуацию, где у вас было бы два интерфейса, которые должны как-то выполниться внутри класса. В силу гибкости интерфейсов у вас есть два выбора: одиночный класс или внутренний класс:

//: c08:MultiInterfaces.java

// Два способа, как класс может

// реализовать множественные интерфейсы.

interface A {} interface B {}

class X implements A, B {}

class Y implements A { B makeB() { // Анонимный внутренний класс:

return new B() {}; } }


public class MultiInterfaces { static void takesA(A a) {} static void takesB(B b) {} public static void main(String[] args) { X x = new X(); Y y = new Y(); takesA(x); takesA(y); takesB(x); takesB(y.makeB()); } } ///:~

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

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

//: c08:MultiImplementation.java

// С конкретным или абстарктным классом, внутренние

// классы - единственный путь для достижения эффекта

// "множественная реализация интерфейса."

class C {} abstract class D {}

class Z extends C { D makeD() { return new D() {}; } }

public class MultiImplementation { static void takesC(C c) {} static void takesD(D d) {} public static void main(String[] args) { Z z = new Z(); takesC(z); takesD(z.makeD()); } } ///:~

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

  • Внутренний класс может иметь несколько экземпляров, каждый со своими собственными данными, независимыми от объекта внешнего класса.
  • В одном внешнем классе может быть несколько внутренних классов, каждый из которых реализует тот же самый интерфейс или наследует от того же самого класса, но по другому. Пример такого применения будет предоставлен дальше.
  • Место создания объекта внутреннего класса не связано с созданием объекта внешнего класса.
  • Не возникает потенциального конфликта в связи "это-есть" (is-a) с внутренним классом, поскольку они раздельные единицы.


  • Пример. Если бы Sequence.java не использовал бы внутренние классы, то Вы бы сказали Sequence это есть Selector, и Вы бы могли иметь только один Selector в Sequence. Так же, у вас не было бы второго метода getRSelector( ), который происходит от Selector, который собственно двигается в обратном направлении по последовательности. Гибкость такого рода доступна только с использованием внутренних классов.


    Содержание раздела