Интерфейсы. Особенности использования в сочетании с классами. Преимущества применения интерфейсов. Ключевые слова interface, implements. Примеры

Интерфейсы. Особенности использования в сочетании с классами. Преимущества применения интерфейсов. Ключевые слова interface, implements. Примеры

Перед изучением данной темы рекомендуется изучить тему:


Содержание



1. Для чего используются интерфейсы? Преимущества применения интерфейсов

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

В интерфейсе указываются имена методов, списки аргументов и типы значений, которые возвращаются, но не тела (реализации) этих методов.

Использование интерфейсов дает следующие взаимосвязанные преимущества:

  • интерфейс фактически определяет «протокол» взаимодействия между классами;
  • объединение интерфейсов и классов позволяет четко отделить объявление общедоступных методов от их реализации;
  • интерфейсы изолируют определение одного или нескольких методов от иерархии наследования. В результате классы, которые не связаны между собой иерархически, могут реализовать один и тот же интерфейс;
  • любой метод, который использует интерфейс, знает только о том, какие методы интерфейса будут вызваны и не больше. Реализация методов осуществляется в классах, которые реализуют этот интерфейс и остается скрытой;
  • сочетание возможностей интерфейсов и классов есть эффективным в случае реализации разнообразных шаблонов проектирования (patterns);
  • интерфейсы позволяют частично реализовать так называемое «множественное наследование», которое было реализовано в суперсложном C++ (хотя эта парадигма и приводила к путанице и появлению многочисленных сложноуловимых ошибок в программировании).

   

2. Общая форма объявления интерфейса. Ключевое слово interface

Общая форма объявления интерфейса имеет следующий вид:

access_modifier interface Name {
    return_type method_name1(parameters1);
    return_type method_name2(parameters2);

    // ...

    type var_name1 = value1;
    type var_name2 = value2;

    // ...
    return_type method_nameN(parametersN);
    type var_nameN = valueN;
}

здесь

  • interface – ключевое слово, которое определяет, что объявляемый элемент языка Java есть интерфейсом;
  • access_modifier – один из модификаторов доступа public или abstract;
  • Name – имя интерфейса;
  • return_type – некоторый тип, который возвращает метод с именем method_name1, method_name2, method_nameN;
  • parameters1, parameters2, parametersN – параметры методов method_name1, method_name2, method_nameN;
  • type – тип переменной, объявленной в интерфейсе;
  • var_name1, var_name2, var_nameN – имена переменных, которые объявлены в интерфейсе. Любая переменная, объявленная в интерфейсе объявляется с модификаторами final и static. Эта переменная должна быть инициализирована. В классе, который реализует интерфейс нельзя изменить значение переменной.

   

3. Какая общая форма реализации интерфейса в классе? Ключевое слово implements

Если интерфейс определен, то его можно реализовать в классе. Общая форма реализации интерфейса в классе имеет вид:

class SomeClass implements SomeInterface {
    // реализация методов интерфейса и других методов класса
    // ...
}

здесь SomeClass – имя класса, реализующего интерфейс SomeInterface.

   

4. Что нужно обязательно выполнить (реализовать) в классе, который реализует заданный интерфейс?

Если класс реализует интерфейс, то для данного класса устанавливаются следующие обязательства:

  • класс обязательно должен содержать реализацию методов, объявленных в этом интерфейсе;
  • сигнатура методов интерфейса должна совпадать с сигнатурой этих же методов, реализованных в классе;
  • методы, которые реализуют элементы интерфейса, обязательно должны быть объявлены в классе как public;
  • в классе запрещено изменять значения переменных, объявленных в интерфейсе.

   

5. Примеры объявление интерфейсов и классов, которые их реализуют

Пример 1. В примере демонстрируется:

  • объявление методов в интерфейсе;
  • объявление переменной в интерфейсе;
  • реализация интерфейса в классе;
  • использование ссылки на интерфейс для доступа к методам.

Пусть в пределах пакета в файле IMathFunctions.java объявлен интерфейс с именем IMathFunctions как показано ниже

// интерфейс, который содержит объявления математических функций
public interface IMathFunctions {
    double pi=3.1415926535; // переменная, число Пи
    int Power(int x, int y); // x в степени y
    double AbsComplex(double real, double imag); // модуль комплексного числа
}

В интерфейсе указываются следующие объявления:

  • переменная pi, которая определяет число Пи. Эта переменная неявно объявлена как final и static. Если некоторый класс реализует этот интерфейс, то изменять значения этой переменной нельзя;
  • математическая функция Power(), предназначенная для возведения числа в степень;
  • математическая функция AbsComplex(), возвращающая модуль комплексного числа.

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

// класс, который реализует интерфейс IMathFuncitons
public class MathFunctions implements IMathFunctions {
    // Реализация методов интерфейса IMathFunctions
    // 1. Модуль комплексного числа
    public double AbsComplex(double real, double imag) {
        double abs;
        abs = Math.sqrt(real*real+imag*imag);
        return abs;
    }

    // 2. Возведение в степень целочисленных значений
    public int Power(int x, int y) {
        int p, i;
        p = 1;
        for (i=1; i<=y; i++)
            p=p*x;
        return p;
    }

    // Внутренние методы класса
    // Определение длины окружности
    public double Circumference(double radius) {
        return 2*pi*radius; // используется переменная pi из интерфейса
    }
}

Использование интерфейса в классе может быть, например, следующим

// использование интерфейса
IMathFunctions mf = new MathFunctions(); // объявить экземпляр класса MathFunctions
//IMathFunctions mf2 = new IMathFunctions(); ошибка - запрещено объявлять экземпляр интерфейса

// вызов интерфейсных методов через экземпляр mf
int d;
double x;
d = mf.Power(3, 4); // d = 81
x = mf.AbsComplex(2, 3); // x = 3.605551275463989

double pi;
pi = mf.pi; // pi = 3.1415926535

В вышеприведенном коде сначала объявляется ссылка mf на интерфейс IMathFunctions. Затем для этой ссылки mf создается экземпляр класса MathFuncions. Такая реализация работает, поскольку класс MathFunctions реализует (implements) интерфейс IMathFunctions. С помощью ссылки mf можно вызвать методы интерфейса.

Пример 2. В примере демонстрируется использование интерфейса IStrings, объявляющего методы обработки строки:

  • метод GetNSymbols(), подсчитывающий количество символов в строке;
  • метод ConvertOdd(), возвращающий строку, состоящую из символов лежащих на нечетных позициях.

Класс ProcessStrings реализует интерфейс IStrings.

Реализация интерфейса помещается в файле IStrings.java и имеет следующий вид:

public interface IStrings {
    // подсчитывает количество заданных символов в строке
    int GetNSymbols(char c, String s);

    // образовывает строку, состоящую из символов строки s,
    // которые размещены на нечетных позициях: 1, 3, 5, ...
    String ConvertOdd(String s);
}

Реализация класса описывается в файле ProcessStrings.java и имеет следующий вид

// класс, реализующий интерфейс IStrings
public class ProcessStrings implements IStrings {
    // реализация методов, которые объявлены в интерфейсе
    // вычислить количество символов в строке s, может быть public-метод
    public int GetNSymbols(char c, String s) {
        int i;
        int k = 0;
        for (i=0; i<s.length(); i++)
            if (c==s.charAt(i))
                k++;
        return k;
    }

    // образовывает строку, состоящую из символов строки s,
    // которые размещены на нечетных позициях: 1, 3, 5, ...
    public String ConvertOdd(String s) {
        String s2="";
        int i;
        for (i=0; i<s.length(); i++) {
            if (i%2==1) // если нечетная позиция
                s2=s2+s.charAt(i);
        }
        return s2;
    }

    public static void main(String[] args) {
        // демонстрация работы с интерфейсом IStrings
        IStrings obj = new ProcessStrings();
        String s1 = "bestprog.net";
        String s2;
        int k;

        s2 = obj.ConvertOdd(s1); // s2 = "etrgnt"
        k = obj.GetNSymbols('t', s1); // k = 2

        System.out.println("s2 = " + s2);
        System.out.println("k = " + k);
    }
}

Как видно из кода, в классе ProcessStrings помещаются реализации методов GetNSymbols() и ConvertOdd(). Статическая функция main() класса ProcessStrings есть точкой входа в программу. В результате выполнения функции main(), будет выведен следующий результат:

s2 = etrgnt
k = 2

   

6. Можно ли создать экземпляр (объект) интерфейса?

Поскольку, интерфейс содержит только объявление без реализации, то создать экземпляр интерфейса запрещено. Однако, ссылку на интерфейс объявить можно. Но при выделении памяти оператором new должен указываться конструктор класса, который реализует данный интерфейс.

   

7. Во скольких классах может быть реализованн интерфейс?

Интерфейс может быть реализован в любом количестве классов. Например, в нижеследующем коде два разных класса с именами SomeClass1, SomeClass2 реализуют один интерфейс SomeInterface.

// некоторый интерфейс
interface SomeInterface {
    // ...
    void method1(int d);
    double method2(double x);
}

// класс, который реализует интерфейс
class SomeClass1 implements SomeInterface {
    // ...
    void method1(int d) {
        // реализация метода
        // ...
    }

    double method2(double x) {
        // реализация метода
        // ...
    }
}

// другой класс, который реализует интерфейс
class SomeClass2 implements SomeInterface {
    // ...
    void method1(int d) {
        // реализация метода
        // ...
    }

    double method2(double x) {
        // реализация метода
        // ...
    }
}

   

8. Сколько интерфейсов может реализовать один класс?

Класс может реализовать любое количество интерфейсов. В этом случае имена интерфейсов разделяются запятой. Например, в следующем коде

class ClassName implements Interface1, Interface2, Interface3 {
    // ...
}

класс с именем ClassName реализует интерфейсы с именами Interface1, Interface2, Interface3.

   

9. Какой тип доступа обязательно должны иметь методы, объявленные в интерфейсе?

Методы, объявленные в интерфейсе, должны быть общедоступными, то есть должны иметь тип доступа public.

   

10. Какой тип доступа по умолчанию имеют методы, объявленные в интерфейсе?

Если не указан тип доступа в объявлении метода, то эти методы имеют тип доступа public. Например, в нижеследующем коде

interface SomeInterface {
    public void method1(int d);
    int method2(int x, int y);
}

оба метода method1() и method2() имеют тип доступа public.

   

11. Как вызвать методы класса с помощью ссылок на интерфейсы?

Последовательность шагов следующая (см. пример с п. 5):

  1. Объявить ссылку на интерфейс
  2. Выделить память для ссылки, указав конструктор класса, реализующий данный интерфейс.
  3. Вызвать методы или переменные, объявленные в интерфейсе.

Например. Пусть задан интерфейс с именем I и класс с именем C. В интерфейсе I объявляется метод

interface I {
    void method();
    int d = 5;
}

class C implements I {
    // ...
    // реализация метода интерфейса I
    public void method() {
        // ...
    }
}

Использование метода method() с учетом интерфейса I может быть следующей:

I obj; // 1. Объявить ссылку
obj = new C(); // 2. Создать экземпляр, используя конструктор класса
obj.method(); // 3. Вызвать интерфейсный метод
int t = obj.d; // 3. Вызвать интерфейсную переменную

 

12. Требования к объявлению переменных в интерфейсах

В интерфейсе можно объявлять переменную. Любая переменная, объявленная в интерфейсе неявно объявляется с модификаторами final и static. Эта переменная должна быть инициализирована. В классе, реализующем интерфейс, нельзя изменить значение переменной.

   

13. В каких случаях можно применять спецификатор доступа public в объявлении интерфейса?

Спецификатор доступа public можно применять перед именем интерфейса только в случае, когда интерфейс определен в файле, который имеет то же имя. Если имя интерфейса не совпадает с именем файла, то спецификатор public нельзя использовать в объявлении интерфейса.

Например. Задан файл с именем IMyInterface.java. В этом файле можно объявить public-интерфейс с таким именем

public interface MyInterface {
    // ...
}

   

14. Как добавить объявление интерфейса в Java Eclipse?

В системах, поддерживающих программирование на языке Java, добавление интерфейсов в проект реализовано с помощью специальных команд.

Чтобы добавить интерфейс в некоторый проект в системе программирования Java Eclipse нужно выполнить следующие шаги.

1. Активировать проект, в который нужно добавить интерфейс (рис. 1).

Java Eclipse Активация проекта

Рис. 1. Активация проекта с именем Project15 в Java Eclipse

2. Вызвать команду создания интерфейса

File->New->Interface

Java Eclipse Команда добавления интерфейса к проекту

Рис. 2. Команда добавления интерфейса к проекту

В результате откроется окно «New Java Interface» (рисунок 3).

3. В окне «New Java Project» нужно выполнить следующие действия (см. рисунок 3):

  • в поле «Source folder:» нужно проверить имя папки с проектом. В нашем случае имя папки с проектом «Project15/src». По желанию можно установить другую папку;
  • в поле «Name:» нужно задать имя интерфейса, например, MyInterface.

Java Eclipse Окно "New Java Interface"

Рис. 3. Окно «New Java Interface». Установление проекта Project15 и имени интерфейса MyInterface

4. После выбора кнопки Finish интерфейс с именем MyInterface будет добавлен к проекту Project15 как показано на рисунке 4.

Java Eclipse Отображение интерфейс окно Package Explorer

Рис. 4. Отображение интерфейса в окне Package Explorer и окне MyInterface.java

Для интерфейса создается отдельный файл MyInterface.java. По всей видимости, имя файла интерфейса и имя интерфейса совпадают. Также интерфейс объявлен как public для того, чтобы он был доступным из других файлов, пакетов.

   


Связанные темы