Что такое интерфейс в java
Перейти к содержимому

Что такое интерфейс в java

  • автор:

Интерфейсы в Java и немного о полиморфизме

Что такое интерфейс Java и каким он бывает. Что такое функциональный интерфейс. Разбираем на примерах.

17 ноября 2017 2 минуты 49632

Автор статьи
Мария Лисянская

Автор статьи
Мария Лисянская
https://gbcdn.mrgcdn.ru/uploads/post/1315/og_cover_image/ba24b43d7eb600c0b99617f40f8c21cc

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

Новички часто спрашивают, чем интерфейс отличается от абстрактного класса. Интерфейсы в Java компенсируют отсутствие множественного наследования классов. У класса-потомка может быть только один абстрактный класс-родитель, а вот интерфейсов класс может применять (имплементировать) сколько угодно.

Интерфейс на Java объявляют примерно так же, как и класс:

public interface MyInterface < void do_something()< // . > default void say_goodbye(String userName) < System.out.println("Пока, "+userName+"! Заходи ещё."); > > 

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

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

Функциональный интерфейс Java

Если у интерфейса только один абстрактный метод, перед нами функциональный интерфейс. Его принято помечать аннотацией @FunctionalInterface, которая указывает компилятору, что при обнаружении второго абстрактного метода в этом интерфейсе нужно сообщить об ошибке. Стандартных (default) методов у интерфейса может быть множество – в том числе принадлежащих классу java.lang.Object.

Как выглядит функциональный интерфейс на Java:

@FunctionalInterface public interface GaMechanic < void new_deck(); default int deal_cards(int num_of_players) < // тело метода >default int check_your_cards(int[] hand) < //. >default int battle(Card player1_Card, Card player2_Card) < //. >> 

Функциональные интерфейсы появились в Java 8. Они обеспечили поддержку лямбда-выражений, использование которых делает код лаконичным и понятным:

button.setOnAction(event -> // если происходит событие System.out.println("Обрабатываем нажатие кнопки.")); 

В той же версии появились пакеты встроенных интерфейсов: java.util.function и java.util.stream.

Реализация интерфейсов классами Java

Допустим, есть интерфейс Edible, которым пользуются классы Fruit, Vegetable, Fish. Экземпляры этих классов можно создавать так:

Edible a1 = new Fruit("Яблоко", “Антоновка”); Edible a2 = new Fish("Нерка слабосолёная", “Тихий океан”, 150); 

Хорошим тоном считается давать интерфейсам названия с окончанием -able/-ible — это показывает, что с объектами, имплементирующими интерфейс, можно что-то делать: Edible (можно есть), Moveable (можно двигать), Clickable (реагирует на клик) и т.д.

Обратите внимание на разницу в конструкторах: для фруктов задаём название и сорт, для рыбы – название, район вылова и вес порции в граммах. Но ссылки на оба объекта храним в переменных одного типа – «Съестное».

Интерфейсы и полиморфизм

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

В Java полиморфизм можно реализовать через:

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

Интерфейс выручает в ситуации, когда при создании переменной мы не знаем, объект какого класса ей будет присвоен.

Интерфейсы в Java

Сегодня у вас прямо день знаний. Еще одна новая и интересная тема — это интерфейсы.

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

interface Кошачьи < void мурчать(); void мяукать(); void рычать(); >

Вот несколько полезных фактов об интерфейсах:

1. Объявление интерфейса

interface Drawable < void draw(); >interface HasValue
  1. Вместо слова class пишем interface .
  2. Содержит только абстрактные методы (слово abstract писать не нужно)
  3. На самом деле у интерфейсов все методы – public

Интерфейс может наследоваться только от интерфейсов. Зато родителей у интерфейса может быть много. Еще говорят, что в Java есть множественное наследование интерфейсов. Примеры:

interface Element extends Drawable, HasValue 

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

Класс может наследоваться от нескольких интерфейсов (и только от одного класса). При этом используется ключевое слово implements . Пример:

abstract class ChessItem implements Drawable, HasValue < private int x, y, value; public int getValue() < return value; >public int getX() < return x; >public int getY() < return y; >>

Класс ChessItem объявлен абстрактным: он реализовал все унаследованные методы, кроме draw . Т.е. класс ChessItem содержит один абстрактный метод — draw() .

Технически между словами extends и implements нет никакой разницы: и то, и то — это наследование. Так было сделано, чтобы повысить читабельность кода. В английском языке принято говорить, что классы наследуются ( extends ), а интерфейсы реализуются ( implements )

4. Переменные

И самое важное: в интерфейсах нельзя объявлять переменные (хотя статические можно).

А зачем же нужны интерфейсы? Когда их используют? У интерфейсов есть два сильных преимущества по сравнению с классами, которые мы рассмотрим далее.

2. Отделение «описания методов» от их реализации.

Раньше мы уже рассказывали, что если вы хотите разрешить вызывать методы своего класса из других классов, то их нужно пометить ключевым словом public . Если же хотите, чтобы какие-то методы можно было вызывать только из этого же класса, их нужно помечать ключевым словом private. Другими словами, мы делим методы класса на две категории: «для всех» и «только для своих».

С помощью интерфейсов это деление можно усилить еще больше. Мы сделаем специальный «класс для всех», и второй «класс для своих», который унаследуем от первого. Вот как это примерно будет:

class Student < private String name; public Student(String name) < this.name = name; >public String getName() < return this.name; >private void setName(String name) < this.name = name; >>
interface Student < public String getName(); >class StudentImpl implements Student < private String name; public StudentImpl(String name) < this.name = name; >public String getName() < return this.name; >private void setName(String name) < this.name = name; >>
public static void main(String[] args) < Student student = new Student("Alibaba"); System.out.println(student.getName()); >
public static void main(String[] args) < Student student = new StudentImpl("Ali") System.out.println(student.getName()); >

Мы разбили наш класс на два: интерфейс и класс , унаследованный от интерфейса . И в чем тут преимущество?

Один и тот же интерфейс могут реализовывать (наследовать) различные классы. И у каждого может быть свое поведение. Так же, как ArrayList и Li nkedList — это две различные реализации интерфейса List .

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

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

3. Множественное наследование

В Java все классы могут иметь только один класс-родитель. В других языках программирования классы часто могут иметь несколько классов-родителей. Это очень удобно, но приносит также много проблем.

В Java пришли к компромиссу: запретили множественное наследование классов, но разрешили множественное наследование интерфейсов. Интерфейс может иметь несколько интерфейсов-родителей. Класс может иметь несколько интерфейсов-родителей и только один класс-родитель.

Почему же множественное наследование классов запретили, а интерфейсов — разрешили? Все дело в так называемом пирамидальном наследовании:

Множественное наследование

Когда класс B наследуется от класса A, он ничего не знает о классах C и D. Поэтому он использует переменные класса A так, как считает нужным. Класс C делает то же самое: использует переменные класса A, но уже другим способом. И в классе D это все выливается в конфликт.

Давайте рассмотрим такой простой пример. Допустим, у нас есть 3 класса:

class Data
class XCoordinate extends Data < public void setX (int x) < value = x;>public int getX () < return value;>>
class YCoordinate extends Data < public void setY (int y) < value = y;>public int getY () < return value;>>

Класс Data хранит у себя переменную value . Его класс-наследник XCoordinate использует ее для того, чтобы хранить в ней переменную x , а класс-наследник YCoordinate использует ее для того, чтобы хранить в ней переменную y .

И это работает. По отдельности. Но вот если мы захотим унаследовать класс XYCoordinates от обоих классов, XCoordinate и YCoordinate , получим неработающий код. У этого класса будут методы его классов-предков, но работать они будут неправильно, т.к. переменная value у них одна.

А т.к. интерфейсам запрещено иметь переменные, то и конфликта такого рода у них быть не может. Поэтому разрешено множественное наследование интерфейсов.

Что такое интерфейсы

Java-университет

Интерфейс — это публичный общепринятый контракт (соглашение), описывающий некоторое поведение. Для чего они нужны? Например, пусть у нас есть ссылка указывающая на список строк. Предположим этот в этот список в начале добавляется множество элементов, а затем, весь список обрабатывается один раз. Допустим мы решили последовать известной рекомендации и использовать класс ArrayList :

ArrayList list = new ArrayList<>();

Мы написали поведение программы в 100500 строк кода, где использовался этот список строк, и для оптимизации работы использовали методы специфичные для класса ArrayList . Например ensureCapacity(). Пока строки добавляются в конец списка, у нас всё прекрасно и быстро работает. Но вот у нас возникла потребность переориентировать нашу программу на немного другой вид работ, где строки добавляются преимущественно в начало списка. Для такого характера нагрузки гораздо больше подходит LinkedList . Но если мы захотим перевести свою программу из 100500 строк кода на рельсы LinkedList , то нам понадобится отыскать и убрать использование специфичных для ArrayList методов, возможно местами сильно меняя логику отдельных участков программы. Если бы мы использовали только те методы, которые имеются и в ArrayList , и в LinkedList , то нам бы этого делать не пришлось. Мы бы могли всего лишь изменить одну строчку кода — объявление списка:

LinkedList list = new LinkedList<>();

Мы можем заметить, что было бы удобней вынести объявление методов общих, для этих классов, в класс-предок, возможно абстрактный, например AbstractList . В этом случае мы могли бы объявить наш список так:

AbstractList list = new ArrayList<>();

И смогли бы быстро переключить реализацию вот так:

AbstractList list = new LinkedList<>();

Но в этом случае, классы, которые мы можем использовать в нашей программе, ограничены только потомками класса AbstractList , даже если если есть более подходящие под задачу классы, не являющиеся потомками AbstractList , но имеющие те же методы с тем же поведением. Как быть? Именно поэтому были изобретены интерфейсы. Интерфейс — это такое соглашение о наборе методов и их поведении, которое могут обязаться соблюдать совершенно не связанные классы, позволяя ссылаться на любой из них с помощью единой ссылки. Например так:

List list; list = new ArrayList<>(); list = new LinkedList<>(); list = new AnotherListClass<>();
  • Интерфейс это контракт (соглашение) о поведении.
  • Множество классов, даже не связанных отношением наследования, могут объявить, что они обязуются соблюдать этот контракт (имплементация интерфейса).
  • Вынесение описания поведения в отдельный интерфейс очень удобно, так как увеличивает гибкость кода, позволяя переключать реализацию (класс имплементирующий интерфейс) интерфейса, на классы не связанные отношением наследования.

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

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

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

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

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

Интерфейс похож на класс в следующих отношениях –

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

Интерфейс может содержать любое количество методов.

Интерфейс записывается в файл с расширением .java , имя интерфейса которого совпадает с именем файла.

Байт-код интерфейса появляется в файле .class .

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

Тем не менее, интерфейс отличается от класса в нескольких отношениях, в том числе –

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

Вы не можете создать экземпляр интерфейса.

Интерфейс не содержит конструкторов.

Все методы в интерфейсе являются абстрактными.

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

Интерфейс не расширен классом; это реализуется классом.

Интерфейс может расширять несколько интерфейсов.

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

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

пример

Ниже приведен пример интерфейса –

/* File name : NameOfInterface.java */ import java.lang.*; // Any number of import statements public interface NameOfInterface  // Any number of final, static fields // Any number of abstract method declarations\ >

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

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

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

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

Методы в интерфейсе неявно общедоступны.

пример

/* File name : Animal.java */ interface Animal  public void eat(); public void travel(); >

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

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

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

пример

/* File name : MammalInt.java */ public class MammalInt implements Animal  public void eat()  System.out.println("Mammal eats"); > public void travel()  System.out.println("Mammal travels"); > public int noOfLegs()  return 0; > public static void main(String args[])  MammalInt m = new MammalInt(); m.eat(); m.travel(); > > 

Это даст следующий результат –

Выход

Mammal eats Mammal travels

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

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

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

Сигнатура метода интерфейса и того же возвращаемого типа или подтипа должна сохраняться при переопределении методов.

Сам класс реализации может быть абстрактным, и если это так, методы интерфейса не должны быть реализованы.

При реализации интерфейсов существует несколько правил –

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

Класс может реализовывать более одного интерфейса одновременно.

Класс может расширять только один класс, но реализовывать множество интерфейсов.

Интерфейс может расширять другой интерфейс, аналогично тому, как класс может расширять другой класс.

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

Интерфейс может расширять другой интерфейс так же, как класс может расширять другой класс. Ключевое слово 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  public void homeGoalScored(); public void visitingGoalScored(); public void endOfPeriod(int period); public void overtimePeriod(int ot); >

Интерфейс Hockey имеет четыре метода, но он наследует два от Sports; таким образом, класс, который реализует Хоккей, должен реализовать все шесть методов. Точно так же класс, который реализует 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 знает, что этот конкретный интерфейс будет использоваться в сценарии делегирования события.

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

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

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