Освой Java играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Интерфейсы

Если нужно создать один или несколько методов, которые будут реализованы в различных классах, то можно освободиться от базового класса. Можно получить тот же конечный результат проще - через интерфейс. Название указывает на его основное использование - определение множества методов, представляющих определённый интерфейс класса, которые могут быть реализованы независимо в различных классах. Все классы затем будут совместно использовать общий интерфейс.

Интерфейс по сути является совокупностью констант и абстрактных методов.

Ключевое слово interface используется для создания полностью абстрактных классов. Создатель интерфейса определяет имена методов, списки аргументов и типы возвращаемых значений, но не тела методов.

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

Чтобы создать интерфейс, используйте ключевое слово interface вместо class. Как и в случае с классами, вы можете добавить перед словом interface спецификатор доступа public (но только если интерфейс определён в файле, имеющем то же имя) или оставить для него дружественный доступ, если он будет использоваться только в пределах своего пакета. Интерфейс может содержать поля, но они автоматически являются статическими (static) и неизменными (final). Все методы и переменные неявно объявляются как public.

Класс, который собирается использовать определённый интерфейс, использует ключевое слово implements. Оно указывает, что интерфейс лишь определяет форму, а вам нужно наполнить кодом. Методы, которые реализуют интерфейс, должны быть объявлены как public.

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

Интерфейсы могут вкладываться в классы и в другие интерфейсы.

Если класс содержит интерфейс, но не полностью реализует определённые им методы, он должен быть объявлен как abstract.

Интерфейсы — это не классы. С помощью ключевого слова new нельзя создать экземпляр интерфейса:


х = new List(...); // Нельзя!

Но можно объявлять интерфейсные переменные:


List<String> catNames; // Можно!

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


List<String> catNames = new ArrayList<>();

Рассмотрим быстрый пример создания интерфейса. Выберите в меню File | New | Interface и придумайте имя для нового интерфейса. В полученной заготовке добавьте два имени метода (только имена, без кода).


package ru.alexanderklimov.quickcourse;

public interface SimpleInterface {
    String getClassName();
    int getAge();
}

Создайте или откройте какой-нибудь класс, к которому нужно применить интерфейс, и добавьте к нему implements SimpleInterface. Среда разработки подчеркнёт красной линией имя класса и предложит добавить методы, которые требуются интерфейсом. Соглашаемся и получаем результат:


package ru.alexanderklimov.quickcourse;

public class Cat extends Animal implements SimpleInterface {

    @Override
    public String getClassName() {
        return null;
    }
    
    @Override
    public int getAge() {
        return 0;
    }
}

Среда разработки сгенерировала два метода и использовала в качестве возвращаемых результатов значения по умолчанию. Это могут быть и нулевые значения и null. Осталось подправить шаблоны созданных методов под свои задачи. Например, так:


@Override
public String getClassName() {
    return "Cat";
}

@Override
public int getAge() {
    return 5;
}

Первый метод возвращает имя класса, а второй - возраст кота (странно, что всем котам будет по пять лет, но это лишь пример).

Здесь важно понять роль интерфейса. Мы лишь придумываем имена, а класс уже реализует нужную задачу. Для примера можно создать в интерфейсе метод play() для класса "Пианино" и класса "Гитара", так как играть можно на обеих инструментах. Но код в методах будет отличаться, так как принцип игры на инструментах совершенно разный.

Константы в интерфейсах

Интерфейсы можно использовать для импорта констант в несколько классов. Вы просто объявляете интерфейс, содержащий переменные с нужными значениями. При реализации интерфейса в классе имена переменных будут помещены в область констант. Поля для констант становятся открытыми и являются статическими и конечными (модификаторы public static final). При этом, если интерфейс не будет содержать никаких методов, то класс не будет ничего реализовывать. Хотя данный подход не рекомендуют использовать.

Расширение интерфейсов

Интерфейс может наследоваться от другого интерфейса через ключевое слово extends.

Методы обратного вызова

Интерфейсы часто используются для создания методов обратного вызова (callback). Рассмотрим такой пример. Создадим новый класс SubClass с интерфейсом MyCallback:


package ru.alexanderklimov.quickcourse;

public class SubClass {
    interface MyCallback{
        void callBackReturn();
    }

    MyCallback myCallback;

    void registerCallBack(MyCallback callback){
        this.myCallback = callback;
    }

    void doSomething(){
        // Здесь какой-то длительный код
        // Например, тянем кота за хвост

        // вызываем метод обратного вызова
        myCallback.callBackReturn();
    }
}

У интерфейса мы определили один метод callBackReturn(). Далее в классе мы создали объект интерфейса и инициализировали его в конструкторе класса. В классе также был создан метод doSomething(), в котором может содержаться какой-то сложный код. В конце метода вызывается метод интерфейса. В данном случае мы сами создали метод и знаем его код. Но во многих случаях, вы будете использовать готовый метод какого-то класса и вы не будете знать, что именно содержится в этом методе. Вам надо только знать, что такой метод существует, например, из документации и он выполняет конкретную задачу.

Переходим в код активности и подключаем интерфейс через ключевое слово implements:


public class MainActivity extends ActionBarActivity implements SubClass.MyCallback {}

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


@Override
public void callBackReturn() {
    
}

Теперь мы можем использовать метод обратного вызова callBackReturn() для решения своих задач. Допустим у нас есть текстовая метка и кнопка. При щелчке выполняется какой-то сложный код из класса SubClass. Когда он закончит работу, то сработает метод обратного вызова callBackReturn(), в котором пропишем нужные действия.


package ru.alexanderklimov.testapplication;

import ...

public class MainActivity extends ActionBarActivity implements SubClass.MyCallback {

    private TextView mResultTextView;
    private SubClass mSubClass;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        mResultTextView = (TextView)findViewById(R.id.textViewResult);

        mSubClass = new SubClass();
        mSubClass.registerCallBack(this);
    }

    public void onClick(View v) {
        mSubClass.doSomething();
    }

    @Override
    public void callBackReturn() {
        mResultTextView.setText("Вызван метод обратного вызова");
    }
}    

Слушатели

Очень часто для интерфейса используют слово Listener, например, у кнопки есть интерфейс OnClickListener.

Мы можем создавать подобные слушатели для собственных классов.

Также интерфейсы часто используются при работе с фрагментами.

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

Реклама