JavaRush /Курсы /Java Syntax Pro /Интерфейсы в Java

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

Java Syntax Pro
17 уровень , 6 лекция
Открыта

1. Знакомство с интерфейсами

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

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

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

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

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

interface Drawable
{
   void draw();
}

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

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

interface Element extends Drawable, HasValue
{
   int getX();
   int getY();
}

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 и LinkedList — это две различные реализации интерфейса List.

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

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


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

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

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

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

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

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

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

class Data
{
   protected int value;
}
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 у них одна.

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


Комментарии (186)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Exaltyr777 Уровень 24
4 августа 2025
Так и не понял из лекции зачем нужны интерфейсы, надеюсь дальше получше объяснят
Anonymous #3585174 Уровень 33
22 июля 2025
Like
15 мая 2025
боже как это все запомнить......
Cryptosin Уровень 24
14 февраля 2025
Почему в Java нет множественного наследования классов, но есть множественное наследование интерфейсов? Java поддерживает множественное наследование интерфейсов, даже если они содержат default-методы (начиная с Java 8), но требует явного разрешения конфликтов. Ответ с учетом default-методов: 1. Проблема ромбовидного наследования (Diamond Problem) Если бы Java поддерживала множественное наследование классов, то возникала бы ситуация, когда два родительских класса содержат метод с одинаковым названием и реализацией. При наследовании от обоих классов компилятор не смог бы однозначно выбрать, какую реализацию использовать. Пример (если бы Java разрешала множественное наследование классов):

class A {
    void show() { System.out.println("A"); }
}

class B extends A {
    void show() { System.out.println("B"); }
}

class C extends A {
    void show() { System.out.println("C"); }
}

// Ошибка: какой метод show() наследует D?
class D extends B, C { }
Здесь D наследуется от B и C, у которых разные версии метода show(). Java не может решить, какую реализацию выбрать, поэтому множественное наследование классов запрещено. 2. Почему интерфейсы можно наследовать множественно? До Java 8 интерфейсы содержали только сигнатуры методов, а их реализацию предоставлял класс, реализующий интерфейс. Это устраняло проблему ромбовидного наследования, потому что компилятор требовал от разработчика самостоятельно определить поведение методов. Но с Java 8 в интерфейсах появились default-методы, содержащие реализацию:

interface A {
    default void show() { System.out.println("A"); }
}

interface B {
    default void show() { System.out.println("B"); }
}

class C implements A, B {
    @Override
    public void show() {
        // Разрешение конфликта
        A.super.show();
    }
}
Cryptosin Уровень 24
14 февраля 2025
Здесь Java требует явного выбора метода, если несколько интерфейсов предоставляют реализацию одного и того же метода. Если интерфейсы не содержат реализацию (abstract-методы), проблем нет, так как класс обязан их реализовать. 3. Как Java разрешает конфликты при множественном наследовании интерфейсов? 1 - Если два интерфейса содержат метод без реализации (abstract), то класс должен сам его реализовать:

interface A {
    void show();
}

interface B {
    void show();
}

class C implements A, B {
    @Override
    public void show() {
        System.out.println("C's implementation");
    }
}
2 - Если два интерфейса содержат default-методы с одинаковыми названиями, компилятор требует явного указания, какую версию использовать:

interface A {
    default void show() { System.out.println("A"); }
}

interface B {
    default void show() { System.out.println("B"); }
}

class C implements A, B {
    @Override
    public void show() {
        // Выбираем реализацию из интерфейса A
        A.super.show();
    }
}
Просто A.show(); не сработает, потому что show() — это нестатический метод интерфейса, а нестатические методы вызываются через объект.

interface A {
    default void show() { System.out.println("A"); }
}

class C implements A {
    @Override
    public void show() {
        A.show(); // Ошибка компиляции!
    }
}
Компилятор выдаст ошибку:

Non-static method 'show()' cannot be referenced from a static context
Проблема в том, что default-методы принадлежат конкретному экземпляру класса, а не интерфейсу как таковому. Почему нужно A.super.show();? Вызов A.super.show(); означает:
Cryptosin Уровень 24
14 февраля 2025
- Использовать реализацию метода из интерфейса A - Обращаться к родительскому интерфейсу, а не к текущему классу

interface A {
    default void show() { System.out.println("A"); }
}

interface B {
    default void show() { System.out.println("B"); }
}

class C implements A, B {
    @Override
    public void show() {
        A.super.show(); // Явно выбираем метод из A
    }
}

public class Main {
    public static void main(String[] args) {
        C obj = new C();
        obj.show(); // Выведет "A"
    }
}
Здесь A.super.show(); позволяет явно указать, что мы хотим использовать метод из A, а не B. Вывод - A.show(); не работает, потому что show() — нестатический метод. - A.super.show(); — единственный правильный способ вызвать default-метод из интерфейса внутри класса. - Это правило работает только внутри методов экземпляра (instance methods), потому что super означает обращение к родителю для текущего объекта. 3 - Если один интерфейс содержит default-метод, а другой объявляет такой же метод abstract, то побеждает abstract-метод, и класс обязан его реализовать:

interface A {
    default void show() { System.out.println("A"); }
}

interface B {
    void show(); // Абстрактный метод
}

class C implements A, B {
    @Override
    public void show() {
        System.out.println("C's own implementation");
    }
}
В этом случае default-метод A игнорируется, так как B требует явной реализации. Вывод 1 - Java запрещает множественное наследование классов, потому что это ведёт к проблемам с ромбовидным наследованием и дублированием состояний. 2 - Множественное наследование интерфейсов разрешено, потому что до Java 8 интерфейсы не содержали реализаций. 3 - С появлением default-методов Java требует от разработчика явно разрешать конфликты при реализации нескольких интерфейсов с одинаковыми методами.
Yasin Akhmadov Уровень 22
16 февраля 2025
Благодарю за подробное пояснение. Теперь, думаю, вопросов нет)
Victor Уровень 36
11 февраля 2025
Так и отвечайте если спросят - Интерфейс — это дитя Абстракции и Полиморфизма.
Orphey Уровень 32
2 октября 2024
Класс реализует интерфейс - implements Класс наследует класс - extends Интерфейс наследует интерфейс - extends
wattcup Уровень 30
1 сентября 2024
А чем интерфейсы отличаются от абстрактных классов??
Mabelod Уровень 30
12 сентября 2024
У интерфейса есть множествнное наследование + нельзя обьявить переменные (только статические)
Ilya Klimchev Уровень 25
25 января 2025
Интерфейс так же как и Класс обозначает тип переменной, но в отличие от Классов интерфейс не содержит НИКАКОЙ логики. Только список методов, которые могут быть вызваны у переменной. Вся логика реализуется в классах (в том числе Абстрактных). Поэтому переменная может быть типизирована и интерфейсом, и абстрактным классом, а объект, ссылка на который кладётся в переменную, может быть типизирован ТОЛЬКО обычным классом, в котором наполнены ВСЕ методы
Cryptosin Уровень 24
14 февраля 2025
Интерфейсы и абстрактные классы — это оба механизма в объектно-ориентированном программировании для создания базовых классов, которые не могут быть инстанцированы напрямую, но их можно использовать для наследования и реализации. Вот основные различия между ними: 1. Методы: - Абстрактный класс может содержать как абстрактные методы (без реализации), так и методы с реализацией. - Интерфейс содержит только абстрактные методы (до Java 8) или методы с дефолтной реализацией (с Java 8). Интерфейсы не могут содержать реализации методов до Java 8, но начиная с этой версии, они могут содержать дефолтные методы с реализацией. 2. Наследование: - Абстрактный класс может наследоваться только от одного класса (Java не поддерживает множественное наследование классов). - Интерфейс может быть реализован несколькими классами, и класс может реализовать несколько интерфейсов, что позволяет обходить ограничение множественного наследования. 3. Модификаторы доступа: - Абстрактный класс может иметь методы с различными уровнями доступа: private, protected, public. - Интерфейс все методы по умолчанию имеют `public`, и нельзя изменить этот модификатор доступа. 4. Конструкторы: - Абстрактный класс может иметь конструкторы. - Интерфейс не может содержать конструкторы. 5. Переменные: - Абстрактный класс может содержать обычные переменные. - Интерфейс может содержать только `public static final` переменные, т.е. константы. 6. Назначение: - Абстрактные классы обычно используются для создания общих базовых классов с частичной реализацией. - Интерфейсы предназначены для определения общего поведения, которое может быть реализовано разными классами без жесткой связи с иерархией классов. Таким образом, выбор между интерфейсом и абстрактным классом зависит от того, что вы хотите достичь: интерфейс лучше использовать для указания на поведение, которое должны реализовывать классы, а абстрактный класс — для создания общей функциональности с возможностью частичной реализации.
mrnght Уровень 28
1 июля 2024
так и не понял, как эти интерфейсы применимы на практике...
Михаил Федосов Уровень 27
14 августа 2024
На практике интерфейс часто используют как контракт - то, что необходимо реализовать в классе. Это позволяет при разработке в команде более безопасно изменять класс под новые нужды проекта. На примере: у вашего конструторского бюро заказали кабину для самолёта, в заказе прописано - должны бать сделаны сиденья, бортовая панель, руль, окна. Благодаря прописанному контракту заказ можно передать от одной команды другой, и не бояться, что в конечном итоге чего-то будет не хватать.
Wren Уровень 29
18 августа 2024
По сути, интерфейс - это дополнительный функционал, который ты обязуешься реализовать. Это, скорее всего, покрывается где-то в других квестах, но как пример, если ты захочешь сохранить состояние объекта (сериализовать), то класс обязан реализовать интерфейс Serializable. Если хочешь, чтобы твои объекты можно было помещать в итератор, то он должен реализовать интерфейс Iterable.
Илья Уровень 3
28 июня 2024
"2. Отделение «описания методов» от их реализации." ничего не понял
25 июля 2024
Реализация - это то, что в фигурных скобках метода. В интерфейсе реализовывать методы не нужны, достаточно указать их, чтобы они обязательно были реализованы в классе, наследующий (реализующий) данный интерфейс. Правда, можно указать стандартную реализацию в интерфейсе. Но об этом вы узнаете позже 😉
Storager Уровень 2
2 мая 2024
Не ну правда, назвали бы основной класс PopDiva - было бы понятней. Как по мне, так современный певец должен быть артистичным и уметь танцевать.... Брр..! Похоже, аналитикам за то и платят, чтобы одни писали ТЗ по-человечески, а другие - не могли трактовать как хотят. А это - та еще задача.