Интерфейсы в Java: Полное руководство для начинающих
- Что такое интерфейс?
- Внутренние интерфейсы
- Расширение интерфейсов
- Интерфейсы маркеры
- Методы по умолчанию в интерфейсах
- Статические методы интерфейса
1. Что такое интерфейс в Java?
Интерфейс это конструкция языка Java, в рамках которой принято описывать абстрактные публичные (abstract public) методы и статические константы (final static).
С помощью интерфейса можно указать, что именно должен выполнять класс его реализующий, но не как это делать. Способ реализации выбирает сам класс. Интерфейсы не способны сохранять данные состояния. Интерфейсы - это один из механизмов реализации принципа полиморфизма "один интерфейс, несколько методов".
Рассмотрим следующую картинку. У нас есть контракт (интерфейс), в котором описано какие действия должна выполнять мышка. Это например, клик по правой клавише и клик по левой. Разные производители мышки (классы), реализующие данный контракт (интерфейс), обязаны спроектировать мышки, у которых будут эти действия. Но как выглядят мышки, какие дополнительные опции будут иметь - все это решает сам производитель.

Интерфейсы, как и классы могут быть объявлены с уровнем доступа public или default.
Переменные интерфейса являются public static final по умолчанию и эти модификаторы необязательны при их объявлении. Например, в следующем примере объявлены переменные RIGHT, LEFT, UP, DOWN без каких-либо модификаторов. Но они будут public static final.
Все методы интерфейса являются public abstract и эти модификаторы тоже необязательны. Объявляемые методы не содержат тел, их объявления завершаются точкой с запятой:
public interface Moveable {
int RIGHT = 1;
int LEFT = 2;
int UP = 3;
int DOWN = 4;
void moveRight();
void moveLeft();
} Чтобы указать, что данный класс реализует интерфейс, в строке объявления класса указываем ключевое слово implements и имя интерфейса. Класс реализующий интерфейс должен содержать полный набор методов, определенных в этом интерфейсе. Но в каждом классе могут быть определены и свои методы. Например, следующий класс Transport реализует интерфейс Moveable. В нем реализованы методы moveRight() и moveLeft() интерфейса Moveable, и добавлены свои методы stop(), start():
public class Transport implements Moveable {
public void moveRight() {
System.out.println("Транспорт поворачивает вправо.");
}
public void moveLeft() {
System.out.println("Транспорт поворачивает влево.");
}
public void stop() {
System.out.println("Транспорт останавливается.");
}
public void start() {
System.out.println("Транспорт стартует.");
}
} Один интерфейс может быть реализован любым количеством классов. Например, в следующей схеме добавлены еще два класса Robot и Device, которые тоже реализуют интерфейс Moveable.

Класс Robot из вышеуказанной схемы:
public class Robot implements Moveable {
public void moveRight() {
System.out.println("Робот поворачивает вправо.");
}
public void moveLeft() {
System.out.println("Робот поворачивает влево.");
}
} Если класс реализует интерфейс, но не полностью реализует определенные в нем методы, он должен быть объявлен как abstract.
Например, класс Device реализует только один метод интерфейса Moveable, поэтому он абстрактный:
public abstract class Device implements Moveable {
public void moveRight() {
System.out.println("Девайс поворачивает вправо.");
}
} Тип интерфейса можно указывать при объявлении переменных, содержащих ссылки на объекты, классы которых реализуют этот интерфейс. Например, в следующем примере переменная moveable имеет тип Moveable, и указывает она на объект Transport. Но на основе интерфейсов нельзя порождать объекты. Например, в строке Moveable moveable1 = new Moveable() будет ошибка компиляции. При использовании переменной типа интерфейс, доступны только те члены класса, которые определены в этом интерфейсе. Например, нельзя вызвать метод start(), используя переменную moveable. А для переменной transport можно:
public class TransportExample {
public static void main(String[] args) {
Moveable moveable = new Transport();
Transport transport = new Transport();
Moveable robot = new Robot();
//Moveable moveable1 = new Moveable();
// moveable.start();
moveable.moveRight();
moveable.moveLeft();
System.out.println();
transport.start();
transport.moveRight();
transport.moveLeft();
transport.stop();
System.out.println();
robot.moveLeft();
robot.moveRight();
}
} Один класс может реализовать любое количество интерфейсов. На следующей схеме показан класс Pickup, который реализует два интерфейса CargoAuto и PassengersAuto:

public interface CargoAuto {
void transportCargo();
} public interface PassengersAuto {
void transportPassengers();
} Для указания того, что класс реализует несколько интерфейсов, после ключевого слова implements через запятую перечисляются нужные интерфейсы. Класс Pickup должен определить все методы реализуемых интерфейсов:
public class Pickup implements CargoAuto, PassengersAuto {
public void transportCargo() {
System.out.println("Везу груз");
}
public void transportPassengers() {
System.out.println("Везу пассажиров");
}
} 2. Внутренние интерфейсы
Интерфейсы объявленные в классах или в других интерфейсах называются внутренние или вложенные. Например, интерфейс NestedIf определен внутри класса A:
public class A {
public interface NestedIf {
boolean isNotNegative(int x);
}
} При обращении к интерфейсу NestedIf требуется указывать имя его внешнего класса - A.NestedIf:
public class B implements A.NestedIf {
public boolean isNotNegative(int x) {
return x >= 0;
}
} public class NestedIfExample {
public static void main(String[] args) {
A.NestedIf nif = new B();
if (nif.isNotNegative(10)) {
System.out.println("Число 10 не отрицательное.");
}
if (nif.isNotNegative(-12)) {
System.out.println("Этo не будет выведено.");
}
}
} 3. Расширение интерфейсов
Интерфейс может наследоваться от другого интерфейса через ключевое слово extends. Один интерфейс, в отличие от классов, может расширять несколько интерфейсов.
Например, интерфейс Football расширяет интерфейсы TVProgram и Sport. Класс FootballImpl, реализующий интерфейс Football, должен переопределить методы всех трех интерфейсов Football, TVProgram и Sport:

public interface Sport {
void setHomeTeam(String name);
void setVisitingTeam(String name);
} public interface Hockey extends Sport {
void homeGoalScored();
void visitingGoalScored();
void endOfPeriod(int period);
void overtimePeriod(int ot);
} public interface TVProgram {
void switchToChannel();
} public interface Football extends Sport, TVProgram {
void homeTeamScored(int points);
void visitingTeamScored(int points);
void endOfQuarter(int quarter);
} public class FootballImpl implements Football {
@Override
public void setHomeTeam(String name) {
System.out.println("Setting Home Team");
}
@Override
public void switchToChannel() {
System.out.println("Switching to channel");
}
@Override
public void homeTeamScored(int points) {
System.out.println("Scored");
}
@Override
public void setVisitingTeam(String name) {
System.out.println("Setting visiting team");
}
@Override
public void visitingTeamScored(int points) {
System.out.println("Visiting Team Scored");
}
@Override
public void endOfQuarter(int quarter) {
System.out.println("End of quarter");
}
} 4. Интерфейсы маркеры
Интерфейсы маркеры - это интерфейсы, у которых не определены ни методы, ни переменные. Реализация этих интерфейсов придает классу определенные свойства. Например, интерфейсы Cloneable и Serializable, отвечающие за клонирование и сохранение объекта в информационном потоке, являются интерфейсами маркерами. Если класс реализует интерфейс Cloneable, это говорит о том, что объекты этого класса могут быть клонированы.
5. Методы по умолчанию в интерфейсах
В JDK 8 в интерфейсы ввели методы по умолчанию - это методы, у которых есть реализация. Другое их название - методы расширения. Классы, реализующие интерфейсы, не обязаны переопределять такие методы, но могут если это необходимо. Методы по умолчанию определяются с ключевым словом default.
Интерфейс SomeInterface объявляет метод по умолчанию defaultMethod() с базовой реализацией:
public interface SomeInterface {
default String defaultMethod() {
return "Объект типа String по умолчанию";
}
} Класс SomeInterfaceImpl1, реализующий этот интерфейс, не переопределяет метод defaultMethod() - так можно.
public class SomeInterfaceImpl1 implements SomeInterface {
} А если класс SomeInterfaceImpl2 не устраивает реализация по умолчанию, он переопределяет этот метод:
public class SomeInterfaceImpl2 implements SomeInterface {
@Override
public String defaultMethod() {
return "Другая символьная строка";
}
} Создаем два объекта классов SomeInterfaceImpl1 и SomeInterfaceImpl2, и вызываем для каждого метод defaultMethod(). Для объекта класса SomeInterfaceImpl1 вызовется метод, реализованный в интерфейсе, а для объекта класса SomeInterfaceImpl2 - его собственная реализация:
public class DefaultMethodExample {
public static void main(String[] args) {
SomeInterface obj1 = new SomeInterfaceImpl1();
SomeInterface obj2 = new SomeInterfaceImpl2();
System.out.println(obj1.defaultMethod());
System.out.println(obj2.defaultMethod());
}
} Результат выполнения:
Объект типа String по умолчанию
Другая символьная строка 6. Статические методы интерфейса
В версии JDK 8, в интерфейсы добавлена еще одна возможность - определять в нем статические методы. Статические методы интерфейса, как и класса, можно вызывать независимо от любого объекта. Для вызова статического метода достаточно указать имя интерфейса и через точку имя самого метода.
public interface MyIf {
int getNumber();
static int staticMethod() {
return 0;
}
} public class StaticMethodExample {
public static void main(String[] args) {
MyIf obj1 = new MyIfImp();
System.out.println(obj1.getNumber());
System.out.println(MyIf.staticMethod());
}
}
Курс 'Java для начинающих' на Udemy
Please log in or register to have a possibility to add comment.