Перейти к содержимому

Для чего интерфейсы в java

  • автор:

Интерфейсы

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

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

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

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

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

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

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

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

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

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

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

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

 List catNames; // Можно! 

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

 List catNames = new ArrayList<>(); 

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

 package ru.alexanderklimov.quickcourse; public interface SimpleInterface

Создайте или откройте какой-нибудь класс, к которому нужно применить интерфейс, и добавьте к нему 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()

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

Здесь важно понять роль интерфейса. Мы лишь придумываем имена, а класс уже реализует нужную задачу. Для примера можно создать в интерфейсе метод 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.

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

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

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

28. Java – Интерфейсы

Интерфейс – это ссылочный тип в Java. Он схож с классом. Это совокупность абстрактных методов. Класс реализует интерфейс, таким образом наследуя абстрактные методы интерфейса.

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

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

Написание интерфейса схоже с написанием класса. Но класс описывает атрибуты и поведения объекта. И интерфейс содержит поведения, которые класс реализует.

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

Чем похожи класс и интерфейс?

Интерфейс схож с классом следующим образом:

  • Интерфейс может содержать любое количество методов.
  • Интерфейс записан в файле с расширением .java, и имя интерфейса совпадает с именем файла.
  • Байт-код интерфейса находится в .class файле.
  • Интерфейсы появляются в пакетах, и их соответствующий файл байт-кода должен быть в структуре каталогов, которая совпадает с именем пакета.

Чем отличается класс от интерфейса?

Однако, интерфейс всё же отличается от класса. Отличие интерфейса от класса в Java:

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

Объявление интерфейсов

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

Пример 1

/* File name : NameOfInterface.java */ import java.lang.*; // Любое количество запросов импорта public interface NameOfInterface < //создание интерфейса // Любое количество полей final и static // Любое количество объявлений абстрактных методов >

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

  • Интерфейс абстрактный косвенно. Вам не нужно использовать ключевое слово abstract во время объявления интерфейса.
  • Каждый метод в интерфейсе косвенно абстрактным, поэтому ключевое слово abstract не нужно.
  • Методы в интерфейсе косвенно публичны.

Пример 2

/* File name : Animal.java */ interface Animal

Реализация интерфейса

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

Класс использует ключевое слово implements для реализации интерфейса. Ключевое слово implements появляется при объявлении класса в его расширенной части.

Пример

/* File name : MammalInt.java */ public class MammalInt implements Animal < public void eat() < System.out.println("Млекопитающее кушает"); >public void travel() < System.out.println("Млекопитающее путешествует"); >public int noOfLegs() < return 0; >public static void main(String args[]) < MammalInt m = new MammalInt(); m.eat(); m.travel(); >> 
Млекопитающее кушает Млекопитающее путешествует 

При переопределении методов в интерфейсе, нужно следовать некоторым правилам:

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

При реализации интерфейсов есть некоторые правила:

  • Класс может реализовать более одного интерфейса за раз.
  • Класс может расширить только один класс, но реализовать множество интерфейсов.
  • Интерфейс может расширить другой интерфейс таким же образом, как класс расширяет другой класс.

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

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

Приведённый интерфейс Sports расширен интерфейсами Hockey и Football.

Пример

// Filename: Sports.java public interface Sports < public void setHomeTeam(String name); public void setVisitingTeam(String name); >// Filename: Football.java public interface Football extends Sports < public void homeTeamScored(int points); public void visitingTeamScored(int points); public void endOfQuarter(int quarter); >// Filename: Hockey.java public interface Hockey extends Sports

Интерфейс Hockey имеет четыре метода, но он наследует два из Sports; таким образом, класс, который реализует Hockey, должен реализовать все шесть методов. Подобно этому, класс, который реализует Football, должен определить три метода из Football и два метода из Sports.

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

Класс в Java может расширить только один родительский класс. Множественное наследование невозможно. Однако интерфейсы не классы, и интерфейс может расширить более чем один родительский интерфейс.

Ключевое слово extends используется лишь раз, а родительские интерфейсы объявляются через запятую.

Например, если интерфейс Hockey расширил и Sports, и Event, то объявление выглядело бы так:

public interface Hockey extends Sports, Event 

Интерфейсы тегов

Самое распространённое использование расширения интерфейсов происходит тогда, когда родительский интерфейс не содержит каких-либо методов. Например, интерфейс MouseListener в пакете java.awt.event расширил java.util.EventListener, который определяется так:

package java.util; public interface EventListener <> 

Интерфейс без методов в нём называется интерфейсом тегов. Есть две простые дизайнерские цели для интерфейсов тегов:

Создаёт общего родителя – как в случае с интерфейсом EventListener, который расширяется множеством других в Java API, вы можете использовать интерфейс тегов, чтобы создать общего родителя среди группы интерфейсов. Например, когда интерфейс расширяет EventListener, то JVM знает, что этот конкретный интерфейс будет использоваться в сценарии делегирования событий.

Добавляет тип данных в класс – эта ситуация является источником термина «тегирование». Класс, который реализует интерфейс тегов, не должен определять какие-либо методы (т.к. интерфейс не имеет таковых), но класс становится типом интерфейса через полиморфизм.

Оглавление

  • 1. Java – Самоучитель для начинающих
  • 2. Java – Обзор языка
  • 3. Java – Установка и настройка
  • 4. Java – Синтаксис
  • 5. Java – Классы и объекты
  • 6. Java – Конструкторы
  • 7. Java – Типы данных и литералы
  • 8. Java – Типы переменных
  • 9. Java – Модификаторы
  • 10. Java – Операторы
  • 11. Java – Циклы и операторы цикла
  • 11.1. Java – Цикл while
  • 11.2. Java – Цикл for
  • 11.3. Java – Улучшенный цикл for
  • 11.4. Java – Цикл do..while
  • 11.5. Java – Оператор break
  • 11.6. Java – Оператор continue
  • 12. Java – Операторы принятия решений
  • 12.1. Java – Оператор if
  • 12.2. Java – Оператор if..else
  • 12.3. Java – Вложенный оператор if
  • 12.4. Java – Оператор switch..case
  • 12.5. Java – Условный оператор (? 🙂
  • 13. Java – Числа
  • 13.1. Java – Методы byteValue(), shortValue(), intValue(), longValue(), floatValue(), doubleValue()
  • 13.2. Java – Метод compareTo()
  • 13.3. Java – Метод equals()
  • 13.4. Java – Метод valueOf()
  • 13.5. Java – Метод toString()
  • 13.6. Java – Метод parseInt()
  • 13.7. Java – Метод Math.abs()
  • 13.8. Java – Метод Math.ceil()
  • 13.9. Java – Метод Math.floor()
  • 13.10. Java – Метод Math.rint()
  • 13.11. Java – Метод Math.round()
  • 13.12. Java – Метод Math.min()
  • 13.13. Java – Метод Math.max()
  • 13.14. Java – Метод Math.exp()
  • 13.15. Java – Метод Math.log()
  • 13.16. Java – Метод Math.pow()
  • 13.17. Java – Метод Math.sqrt()
  • 13.18. Java – Метод Math.sin()
  • 13.19. Java – Метод Math.cos()
  • 13.20. Java – Метод Math.tan()
  • 13.21. Java – Метод Math.asin()
  • 13.22. Java – Метод Math.acos()
  • 13.23. Java – Метод Math.atan()
  • 13.24. Java – Метод Math.atan2()
  • 13.25. Java – Метод Math.toDegrees()
  • 13.26. Java – Метод Math.toRadians()
  • 13.27. Java – Метод Math.random()
  • 14. Java – Символы
  • 14.1. Java – Метод Character.isLetter()
  • 14.2. Java – Метод Character.isDigit()
  • 14.3. Java – Метод Character.isWhitespace()
  • 14.4. Java – Метод Character.isUpperCase()
  • 14.5. Java – Метод Character.isLowerCase()
  • 14.6. Java – Метод Character.toUpperCase()
  • 14.7. Java – Метод Character.toLowerCase()
  • 14.8. Java – Метод Character.toString()
  • 15. Java – Строки
  • 15.1. Java – Метод charAt()
  • 15.2. Java – Метод compareTo()
  • 15.3. Java – Метод compareToIgnoreCase()
  • 15.4. Java – Метод concat()
  • 15.5. Java – Метод contentEquals()
  • 15.6. Java – Метод copyValueOf()
  • 15.7. Java – Метод endsWith()
  • 15.8. Java – Метод equals()
  • 15.9. Java – Метод equalsIgnoreCase()
  • 15.10. Java – Метод getBytes()
  • 15.11. Java – Метод getChars()
  • 15.12. Java – Метод hashCode()
  • 15.13. Java – Метод indexOf()
  • 15.14. Java – Метод intern()
  • 15.15. Java – Метод lastIndexOf()
  • 15.16. Java – Метод length()
  • 15.17. Java – Метод matches()
  • 15.18. Java – Метод regionMatches()
  • 15.19. Java – Метод replace()
  • 15.20. Java – Метод replaceAll()
  • 15.21. Java – Метод replaceFirst()
  • 15.22. Java – Метод split()
  • 15.23. Java – Метод startsWith()
  • 15.24. Java – Метод subSequence()
  • 15.25. Java – Метод substring()
  • 15.26. Java – Метод toCharArray()
  • 15.27. Java – Метод toLowerCase()
  • 15.28. Java – Метод toString()
  • 15.29. Java – Метод toUpperCase()
  • 15.30. Java – Метод trim()
  • 15.31. Java – Метод valueOf()
  • 15.32. Java – Классы StringBuilder и StringBuffer
  • 15.32.1. Java – Метод append()
  • 15.32.2. Java – Метод reverse()
  • 15.32.3. Java – Метод delete()
  • 15.32.4. Java – Метод insert()
  • 15.32.5. Java – Метод replace()
  • 16. Java – Массивы
  • 17. Java – Дата и время
  • 18. Java – Регулярные выражения
  • 19. Java – Методы
  • 20. Java – Потоки ввода/вывода, файлы и каталоги
  • 20.1. Java – Класс ByteArrayInputStream
  • 20.2. Java – Класс DataInputStream
  • 20.3. Java – Класс ByteArrayOutputStream
  • 20.4. Java – Класс DataOutputStream
  • 20.5. Java – Класс File
  • 20.6. Java – Класс FileReader
  • 20.7. Java – Класс FileWriter
  • 21. Java – Исключения
  • 21.1. Java – Встроенные исключения
  • 22. Java – Вложенные и внутренние классы
  • 23. Java – Наследование
  • 24. Java – Переопределение
  • 25. Java – Полиморфизм
  • 26. Java – Абстракция
  • 27. Java – Инкапсуляция
  • 28. Java – Интерфейсы
  • 29. Java – Пакеты
  • 30. Java – Структуры данных
  • 30.1. Java – Интерфейс Enumeration
  • 30.2. Java – Класс BitSet
  • 30.3. Java – Класс Vector
  • 30.4. Java – Класс Stack
  • 30.5. Java – Класс Dictionary
  • 30.6. Java – Класс Hashtable
  • 30.7. Java – Класс Properties
  • 31. Java – Коллекции
  • 31.1. Java – Интерфейс Collection
  • 31.2. Java – Интерфейс List
  • 31.3. Java – Интерфейс Set
  • 31.4. Java – Интерфейс SortedSet
  • 31.5. Java – Интерфейс Map
  • 31.6. Java – Интерфейс Map.Entry
  • 31.7. Java – Интерфейс SortedMap
  • 31.8. Java – Класс LinkedList
  • 31.9. Java – Класс ArrayList
  • 31.10. Java – Класс HashSet
  • 31.11. Java – Класс LinkedHashSet
  • 31.12. Java – Класс TreeSet
  • 31.13. Java – Класс HashMap
  • 31.14. Java – Класс TreeMap
  • 31.15. Java – Класс WeakHashMap
  • 31.16. Java – Класс LinkedHashMap
  • 31.17. Java – Класс IdentityHashMap
  • 31.18. Java – Алгоритмы Collection
  • 31.19. Java – Iterator и ListIterator
  • 31.20. Java – Comparator
  • 32. Java – Дженерики
  • 33. Java – Сериализация
  • 34. Java – Сеть
  • 34.1. Java – Обработка URL
  • 35. Java – Отправка Email
  • 36. Java – Многопоточность
  • 36.1. Java – Синхронизация потоков
  • 36.2. Java – Межпоточная связь
  • 36.3. Java – Взаимная блокировка потоков
  • 36.4. Java – Управление потоками
  • 37. Java – Основы работы с апплетами
  • 38. Java – Javadoc

Интерфейсы — Java: Введение в ООП

Классы в Java — основы основ, но их описание будет неполным без интерфейсов, с которыми они тесно связаны. Интерфейсы — более простая конструкция, но, как и в случае с классами, полное понимание интерфейсов требует опыта работы с ними. Зачем они нужны? Интерфейсы позволяют задавать требования к классам, то есть какие методы требуются от класса. Предположим, что мы хотим работать в приложении с генератором паролей. Генератор в нашем случае это обычный класс, с методом generate() , возвращающим пароль.

var generator = new SimplePasswordGenerator(); generator.generate(); // Возвращает готовый пароль generator.generate(); // Уже другой пароль 

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

Создадим интерфейс PasswordGenerator и опишем в нем два метода generate() . Один метод будет без параметров, другой — с возможностью настройки длины пароля:

interface PasswordGenerator  String generate(); String generate(int length); > 

Теперь интерфейс нужно реализовать. Делается это в определении класса:

// Ключевое слово implements означает, что класс реализует интерфейс PasswordGenerator class SimplePasswordGenerator implements PasswordGenerator  public String generate()  // Обращаемся к методу объекта // 16 – выбранное значение по умолчанию return this.generate(16); > public String generate(int length)  // Тут логика генерации простого пароля > > 

Интерфейсы не ограничивают класс в его расширении. Помимо интерфейсных методов, мы можем добавить и любые другие.

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

Хорошо, вот мы ввели интерфейс, что он нам дал? Если коротко: полиморфизм, а если точнее — полиморфизм подтипов (subtyping). Прямо сейчас он нам не очень нужен, но для общего развития попробуем ухватить его идею. Осторожно, дальнейший текст может напугать. Если он кажется вам сложным, просто пропустите, все это мы будем повторять еще не раз.

Код, который будет использовать passwordGenerator может выглядеть так:

class UserController  // Здесь создаются пользователи // Этот код вызывается где-то внутри приложения при регистрации пользователя public void create()  // Создаем пользователя // И где-то тут же генерируем ему пароль var generator = new SimplePasswordGenerator(); var password = generator.generate(); > > 

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

var generator; if (userChooseSomething)  generator = new SimplePasswordGenerator(); > else  generator = new SuperPasswordGenerator(); > 

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

Сделаем так, чтобы класс SuperPasswordGenerator() тоже реализовывал интерфейс PasswordGenerator :

class SuperPasswordGenerator implements PasswordGenerator  public String generate()  return this.generate(16); > public String generate(int length)  // Тут уже другая логика генерации супер-сложного пароля > > 

Код, который будет использовать PasswordGenerator , меняется на такой:

class UserController  // Интерфейс = Тип // Вместо конкретного класса указываем интерфейс public void create(PasswordGenerator generator)  var password = generator.generate(); > > 

Интерфейсы в Java — это настоящие типы данных, поэтому их можно указывать в определениях методов.

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

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

Все это уже станет актуальным, как только мы дойдем до Java-коллекций.

Соглашения и правила

К интерфейсам предъявляются такие же требования как и к классам

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов

Наши выпускники работают в компаниях:

Для чего интерфейсы в java

Механизм наследования очень удобен, но он имеет свои ограничения. В частности мы можем наследовать только от одного класса, в отличие, например, от языка С++, где имеется множественное наследование.

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

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

interface Printable

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

Все методы интерфейса не имеют модификаторов доступа, но фактически по умолчанию доступ public , так как цель интерфейса — определение функционала для реализации его классом. Поэтому весь функционал должен быть открыт для реализации.

Чтобы класс применил интерфейс, надо использовать ключевое слово implements :

public class Program < public static void main(String[] args) < Book b1 = new Book("Java. Complete Referense.", "H. Shildt"); b1.print(); >> interface Printable < void print(); >class Book implements Printable < String name; String author; Book(String name, String author)< this.name = name; this.author = author; >public void print() < System.out.printf("%s (%s) \n", name, author); >>

В данном случае класс Book реализует интерфейс Printable. При этом надо учитывать, что если класс применяет интерфейс, то он должен реализовать все методы интерфейса, как в случае выше реализован метод print . Потом в методе main мы можем создать объект класса Book и вызвать его метод print. Если класс не реализует какие-то методы интерфейса, то такой класс должен быть определен как абстрактный, а его неабстрактные классы-наследники затем должны будут реализовать эти методы.

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

Printable pr = new Printable(); pr.print();

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

class Journal implements Printable < private String name; String getName()< return name; >Journal(String name) < this.name = name; >public void print() < System.out.println(name); >>

Класс Book и класс Journal связаны тем, что они реализуют интерфейс Printable. Поэтому мы динамически в программе можем создавать объекты Printable как экземпляры обоих классов:

public class Program < public static void main(String[] args) < Printable printable = new Book("Java. Complete Reference", "H. Shildt"); printable.print(); // Java. Complete Reference (H. Shildt) printable = new Journal("Foreign Policy"); printable.print(); // Foreign Policy >> interface Printable < void print(); >class Book implements Printable < String name; String author; Book(String name, String author)< this.name = name; this.author = author; >public void print() < System.out.printf("%s (%s) \n", name, author); >> class Journal implements Printable < private String name; String getName()< return name; >Journal(String name) < this.name = name; >public void print() < System.out.println(name); >>

Интерфейсы в преобразованиях типов

Все сказанное в отношении преобразования типов характерно и для интерфейсов. Например, так как класс Journal реализует интерфейс Printable, то переменная типа Printable может хранить ссылку на объект типа Journal:

Printable p =new Journal("Foreign Affairs"); p.print(); // Интерфейс не имеет метода getName, необходимо явное приведение String name = ((Journal)p).getName(); System.out.println(name);

И если мы хотим обратиться к методам класса Journal, которые определены не в интерфейсе Printable, а в самом классе Journal, то нам надо явным образом выполнить преобразование типов: ((Journal)p).getName();

Методы по умолчанию

Ранее до JDK 8 при реализации интерфейса мы должны были обязательно реализовать все его методы в классе. А сам интерфейс мог содержать только определения методов без конкретной реализации. В JDK 8 была добавлена такая функциональность как методы по умолчанию . И теперь интерфейсы кроме определения методов могут иметь их реализацию по умолчанию, которая используется, если класс, реализующий данный интерфейс, не реализует метод. Например, создадим метод по умолчанию в интерфейсе Printable:

interface Printable < default void print()< System.out.println("Undefined printable"); >>

Метод по умолчанию — это обычный метод без модификаторов, который помечается ключевым словом default . Затем в классе Journal нам необязательно этот метод реализовать, хотя мы можем его и переопределить:

class Journal implements Printable < private String name; String getName()< return name; >Journal(String name) < this.name = name; >>

Статические методы

Начиная с JDK 8 в интерфейсах доступны статические методы — они аналогичны методам класса:

interface Printable < void print(); static void read()< System.out.println("Read printable"); >>

Чтобы обратиться к статическому методу интерфейса также, как и в случае с классами, пишут название интерфейса и метод:

public static void main(String[] args)

Приватные методы

По умолчанию все методы в интерфейсе фактически имеют модификатор public. Однако начиная с Java 9 мы также можем определять в интерфейсе методы с модификатором private . Они могут быть статическими и нестатическими, но они не могут иметь реализации по умолчанию.

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

public class Program < public static void main(String[] args) < Calculatable c = new Calculation(); System.out.println(c.sum(1, 2)); System.out.println(c.sum(1, 2, 4)); >> class Calculation implements Calculatable < >interface Calculatable < default int sum(int a, int b)< return sumAll(a, b); >default int sum(int a, int b, int c) < return sumAll(a, b, c); >private int sumAll(int. values) < int result = 0; for(int n : values)< result += n; >return result; > >

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

Кроме методов в интерфейсах могут быть определены статические константы:

interface Stateable

Хотя такие константы также не имеют модификаторов, но по умолчанию они имеют модификатор доступа public static final , и поэтому их значение доступно из любого места программы.

public class Program < public static void main(String[] args) < WaterPipe pipe = new WaterPipe(); pipe.printState(1); >> class WaterPipe implements Stateable < public void printState(int n)< if(n==OPEN) System.out.println("Water is opened"); else if(n==CLOSED) System.out.println("Water is closed"); else System.out.println("State is invalid"); >> interface Stateable

Множественная реализация интерфейсов

Если нам надо применить в классе несколько интерфейсов, то они все перечисляются через запятую после слова implements:

interface Printable < // методы интерфейса >interface Searchable < // методы интерфейса >class Book implements Printable, Searchable < // реализация класса >

Наследование интерфейсов

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

interface BookPrintable extends Printable

При применении этого интерфейса класс Book должен будет реализовать как методы интерфейса BookPrintable, так и методы базового интерфейса Printable.

Вложенные интерфейсы

Как и классы, интерфейсы могут быть вложенными, то есть могут быть определены в классах или других интерфейсах. Например:

class Printer < interface Printable < void print(); >>

При применении такого интерфейса нам надо указывать его полное имя вместе с именем класса:

public class Journal implements Printer.Printable < String name; Journal(String name)< this.name = name; >public void print() < System.out.println(name); >>

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

Printer.Printable p =new Journal("Foreign Affairs"); p.print();

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

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

public class Program < public static void main(String[] args) < Printable printable = createPrintable("Foreign Affairs",false); printable.print(); read(new Book("Java for impatients", "Cay Horstmann")); read(new Journal("Java Dayly News")); >static void read(Printable p) < p.print(); >static Printable createPrintable(String name, boolean option) < if(option) return new Book(name, "Undefined"); else return new Journal(name); >> interface Printable < void print(); >class Book implements Printable < String name; String author; Book(String name, String author)< this.name = name; this.author = author; >public void print() < System.out.printf("%s (%s) \n", name, author); >> class Journal implements Printable < private String name; String getName()< return name; >Journal(String name) < this.name = name; >public void print() < System.out.println(name); >>

Метод read() в качестве параметра принимает объект интерфейса Printable, поэтому в этот метод мы можем передать как объект Book, так и объект Journal.

Метод createPrintable() возвращает объект Printable, поэтому также мы можем возвратить как объект Book, так и Journal.

Foreign Affairs Java for impatients (Cay Horstmann) Java Dayly News

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *