Приклади застосування внутрішніх класів в поєднанні з інтерфейсами. Низхідне та висхідне перетворення. Переваги застосування висхідного перетворення

Приклади застосування внутрішніх класів в поєднанні з інтерфейсами. Низхідне та висхідне перетворення. Переваги застосування висхідного перетворення


Зміст


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-класів як описано в п.1. У випадку 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.

 


Зв’язані теми