Примеры применения внутренних классов в сочетании с интерфейсами. Нисходящее и восходящее преобразование. Преимущества применения восходящего преобразования
Содержание
- 1. Понятие нисходящего преобразования
- 2. Особенности реализации восходящего преобразования в случае внутреннего private-класса
- 3. Преимущества применения восходящего преобразования в сочетании с внутренними классами
- 4. Особенности реализации восходящего преобразования в случае внутреннего protected-класса
- 5. Пример, который демонстрирует нисходящее преобразование
- 6. Пример, который демонстрирует восходящее преобразование
- 7. Какое отличие между нисходящим преобразованием и восходящим преобразованием? Пример
- Связанные темы
Поиск на других ресурсах:
1. Понятие нисходящего преобразования
Если в классе OutClass объявляется внутренний класс InClass, который имеет доступ public
class OutClass { public class InClass { // реализация класса InClass // ... // реализация методов класса InClass int method1() { // инструкции метода method1() // ... } int method2() { // инструкции метода method2() // ... } } }
то при нисходящем преобразовании использовать методы method1(), method2() класса InClass можно по следующей схеме
// демонстрация использования нисходящего преобразования // создать объект класса OutClass стандартным способом OutClass oc = new OutClass(); // создать объект класса OutClass.InClass с помощью инструкции .new OutClass.InClass ic = oc.new OutClass.InClass();
Итак, при нисходящем преобразовании важно, чтобы внутренний класс имел доступ public.
⇑
2. Особенности реализации восходящего преобразования в случае внутреннего private-класса
Если нужно использовать преимущества восходящего преобразования для внутреннего класса, то нужно придерживаться нижеследующих шагов. Для примера рассмотрен внутренний класс, который должен предоставить программисту-клиенту два метода в пользование с ограниченным доступом к другим типам и реализациям внутреннего класса. Методы имеют имена method1(), method2().
Последовательность шагов следующая.
1. Объявить private-внутренний класс InClass во внешнем классе OutClass
class OutClass { private class InClass { // реализация класса InClass // ... // реализация методов, которые должны быть доступны программисту-клиенту int method1() { // инструкции метода method1() // ... } int method2() { // инструкции метода method2() // ... } } }
После такого объявления, доступ к внутреннему private-классу InClass имеет только внешний для него класс OutClass. Программист-клиент не имеет доступа к классу InClass. Также невозможно осуществить нисходящее преобразование к закрытому (private) классу InClass путем вызова
OutClass oc = new OutClass(); // OutClass.InClass ic = oc.new InClass(); - это есть ошибка!
2. Объявить интерфейс. На этом шаге нужно объявить интерфейс и выбрать внутренние методы, которые будут доступны программисту-клиенту а также модифицировать объявление класса InClass. Эти методы реализуются в классе InClass по нижеследующему образцу
public interface InterfaceInClass { // интерфейсные методы, которые представляются в пользование программисту-клиенту int method1(); int method2(); } class OutClass { // данные и методы класса OutClass // ... // реализация класса InClass с учетом методов интерфейса InterfaceInClass // добавляется 'implements ' private class InClass implements InterfaceInClass { // ... // реализация интерфейсных методов int method1() { // ... } int method2() { // ... } } }
В вышеприведенном коде программисту-клиенту предоставляются в пользование два метода
int method1(); int mehtod2();
Имена методов и их параметры выбраны наугад.
3. Объявить метод, реализующий восходящее преобразование. На третьем этапе в классе OutClass формируется метод, который возвращает объект класса InClass. После этих изменений, объявление интерфейса InterfaceInClass и класса OutClass будет иметь следующий вид:
public interface InterfaceInClass { // интерфейсные методы, которые предоставляются в пользование программисту-клиенту int method1(); int method2(); } class OutClass { // данные и методы класса OutClass // ... // реализация класса InClass с учетом методов интерфейса private class InClass implements InterfaceInClass { // ... // реализация интерфейсных методов int method1() { // инструкции метода method1() // ... } int method2() { // инструкции метода method2() // ... } } // метод класса OutClass, возвращающий объект интерфейса InterfaceInClass public InterfaceInClass GetInterfaceInClass() { // некоторые инструкции (могут отсутствовать) // ... return new InClass(); // возвратить экземпляр класса InClass } }
Вышеприведенная реализация внутреннего класса InClass во внешнем классе OutClass определяет суть восходящего преобразования. Объект внутреннего класса получается по принципу снизу-вверх.
Для вышеприведенного примера, программист-клиент может использовать внутренний класс по следующей схеме
// получить объект класса OutClass OutClass oc = new OutClass(); // через метод GetInterfaceInClass получить объект ic InterfaceInClass ic = oc.GetInterfaceInClass(); // вызвать метод через полученный объект ic int res; res = ic.method1();
⇑
3. Преимущества применения восходящего преобразования в сочетании с внутренними классами
Реализация восходящего преобразования дает разработчику следующие преимущества:
- запрещается доступ к типам, которым соответствуют внутренние классы;
- скрываются все детали реализации внутреннего класса;
- клиенту-программисту предоставляется доступ только к необходимым (ограниченным) реализациям внутреннего класса;
- для программиста-клиента не будет иметь смысла расширять интерфейс за счет добавления новых методов, так как он не будет иметь доступ к дополнительным методам, которые не принадлежат к открытой части класса;
- компилятор Java будет иметь возможность оптимизировать код.
⇑
4. Особенности реализации восходящего преобразования в случае внутреннего protected-класса
Восходящее преобразование для внутренних protected-классов работает по такому же принципу, как и для внутренних private-классов как описано в п.2. В случае private-классов доступ к внутреннему классу имеет внешний класс. В случае protected-классов доступ к внутреннему классу имеют:
- внешний класс;
- унаследованный от внешнего класс;
- классы собственного пакета, в котором объявлен внешний класс. Спецификатор protected дает доступ в пределах собственного пакета.
Для protected-классов можно сделать нисходящее преобразование только из унаследованного класса.
⇑
5. Пример, который демонстрирует нисходящее преобразование
Объявляется внешний класс Calculation, в котором реализовано множество внутренних классов, которые проводят математические вычисления над числами. Один из внутренних классов называется Complex. Он реализует базовые операции над комплексными числами. В результате объявления класс Calculation имеет вид:
// внешний класс, который содержит реализацию класса Complex class Calculation { // внутренний класс Complex public class Complex { double real; // вещественая часть double imag; // мнимая часть // метод, возвращающий модуль комплексного числа double Abs() { return Math.sqrt(real*real+imag*imag); } } }
Использование класса Complex в другом методе демонстрирует нисходящее преобразование
// нисходящее преобразование // создать объект класса Complex с помощью объекта класса Calculation Calculation calc = new Calculation(); Calculation.Complex comp = calc.new Complex(); double res; comp.imag = 3; comp.real = 5; // вызов метода вычисления модуля комплексного числа res = comp.Abs(); // res = 5.830951894845301
⇑
6. Пример, который демонстрирует восходящее преобразование
Дан класс Calculation, в котором реализован скрытый (private) внутренний класс Complex. Класс Complex дает объявление своего общедоступного метода Abs() с помощью интерфейса IComplex.
Реализация класса Calculation и интерфейса IntComplex следующая:
// интерфейс, содержащий сигнатуру метода Abs() interface IntComplex { double Abs(); } // внешний класс, который содержит внутренний скрытый класс Complex class Calculation { // класс реализует интерфейс IntComplex, обрабатывает комплексные числа private class Complex implements IntComplex { // внутренние данные класса complex private double imag; // мнимая часть комплексного числа private double real; // вещественная часть // конструктор класса с двумя параметрами public Complex(double _imag, double _real) { imag = _imag; real = _real; } // реализация метода, который объявляется в интерфейсе IntComplex public double Abs() { return Math.sqrt(real*real+imag*imag); } } // возвратить объект класса Complex public IntComplex GetComplex(double _imag, double _real) { // возвратить объект внутреннего класса return new Complex(_imag, _real); // вызов конструктора с 2 параметрами } }
Метод GetComplex() класса Calculation возвращает объект (экземпляр) внутреннего класса Complex, реализуя тем самым восходящее преобразование. При создании объекта класса Complex в методе GetComplex() вызывается конструктор с 2-мя параметрами.
Ниже демонстрируется использование класса Complex в другом методе
// восходящее преобразование Calculation calc = new Calculation(); // создать объект внешнего класса IntComplex ic = calc.GetComplex(5, 6); // получить объект внутреннего класса double res; // вызвать метод внутреннего класса res = ic.Abs(); // res = 7.810249675906654
Через интерфейс IntComplex() программист-клиент имеет доступ к методам внутреннего скрытого класса Complex. Доступ осуществляется через объект внешнего класса Calculation.
⇑
7. Какое отличие между нисходящим преобразованием и восходящим преобразованием? Пример
Отличие между восходящим и нисходящим преобразованием проявляется в способе получения объекта внутреннего класса. При нисходящем преобразовании объект внутреннего класса создается с помощью оператора .new путем обращения к имени объекта внешнего класса
// нисходящее преобразование OutClass oc = new OutClass(); // создать объект внешнего класса OutClass.InClass ic = oc.new InClass(); // создать объект внутреннего класса
При восходящем преобразовании объект внутреннего класса получается с помощью специального метода внешнего класса, как показано ниже
OutClass oc = new OutClass();
OutClass.InClass ic = oc.GetObjInClass();
где GetObjInClass() – метод, который создает экземпляр внутреннего класса OutClass.InClass. В этом случае внутренний класс OutClass.InClass должен иметь доступ public или protected для унаследованного класса.
В случае реализации внутреннего класса в сочетании с интерфейсом, программный код восходящего преобразования может выглядеть следующим образом:
OutClass oc = new OutClass();
InterfaceInClass ic = oc.GetInterfaceInClass();
здесь
- OutClass – внешний класс, для которого создается объект с именем oc;
- InterfaceInClass – интерфейс, который объявляет методы внутреннего класса для их использования программистом-клиентом;
- GetInterfaceInClass() – метод, который возвращает объект (конкретный экземпляр) внутреннего класса OutClass.InClass(), который реализует (implements) интерфейс InterfaceInClass.
⇑
Связанные темы
- Вложенные и внутренние классы. Статические вложенные классы. Примеры
- Создание объектов вложенных статических и нестатических классов. Конструкции .this и .new