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 у них одна.

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


Комментарии (208)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
22 августа 2026
Много комментариев что ничего не понятно. Я решаю этот курс, уйдя с курса Java 25. И для меня все понятно, видимо потому что там объяснили это понятнее. Если в двух словах, то интерфейс отличается от абстрактного класса тем, что он не может иметь полей, кроме статичных — а если быть точным, то в интерфейсе переменные всегда public static final, то есть это константы, а не обычные поля объекта (разница от нестатика заключается в том, что нестатик относится к конкретному экземпляру). Так же к одному классу можно подписать сразу несколько интерфейсов, в отличие от классов родителей. Допустим, у вас есть абстрактный класс, как в прошлом примере ChessItem. Мы наследуем от него каждую фигуру, и данный класс описывает стандартное поведение каждой фигуры. Но так же нам надо, чтобы у наших фигур было определённое поведение, допустим, в виде возврата на поле, или ваше выдуманное правило для таких фигур, как слон и конь. Мы вспоминаем, что мы делаем всё через ООП, и понимаем, что хотим описать абстракцию нескольких поведений — допустим, летать и швыряться пешками. Вместо того чтобы прописывать новый абстрактный класс или писать данные методы в ChessItem, мы пишем два интерфейса и реализуем их в нужной нам фигуре. В таком случае, если нужно будет, чтобы какая-то фигура так же кидалась пешками и/или одновременно летала, мы реализуем оба интерфейса у фигуры. Если же нам нужно, чтобы фигура только летала, то мы реализуем у неё только интерфейс полёта, и нам не нужно будет реализовывать ненужный нам класс. И ещё важное правило: если класс реализует интерфейс, он обязан реализовать все его методы, если только этот класс не является абстрактным.
22 августа 2026
Сначала делаем абстрактный класс:

public abstract class ChessItem {
    int x;
    int y;

    abstract void move();
}
Теперь мы понимаем, что не все фигуры умеют летать или кидаться пешками. Поэтому создаём два интерфейса:

public interface Flyable {
    void fly();
}

public interface Throwable {
    void throwSomething();
}
22 августа 2026
Одной фигуре нужно только летать — она реализует один интерфейс:

public class Bishop extends ChessItem implements Flyable {
    @Override
    void move() {
        // обычный ход слона
    }

    @Override
    public void fly() {
        // полёт
    }
}
А другой фигуре нужно и летать, и швыряться пешками — она реализует оба интерфейса:

public class Knight extends ChessItem implements Flyable, Throwable {
    @Override
    void move() {
        // обычный ход коня
    }

    @Override
    public void fly() {
        // полёт
    }

    @Override
    public void throwSomething() {
        // швыряние пешкой
    }
}
Maxim Terletsky Уровень 24
2 августа 2026
Интерфейс - это контракт (договор), который класс обязуется выполнить. Это чистая абстракция: интерфейс описывает ЧТО должен уметь делать объект, но не говорит КАК.

interface Drawable {
    void draw(); // все методы интерфейса по умолчанию public abstract
}
Ключевые отличия от абстрактного класса: • Интерфейс не может хранить состояние объекта. В нём можно объявлять только константы (public static final поля) • Все абстрактные методы интерфейса автоматически являются public abstract. • Интерфейс не может иметь конструкторов • Класс может реализовать много интерфейсов, но унаследовать только один класс Объявление интерфейса

// Интерфейс с одним методом - функциональный интерфейс
interface Drawable {
    void draw();
}

// Интерфейс с несколькими методами
interface HasValue {
    int getValue();
    void setValue(int value);
}

// Интерфейс с константами (все поля автоматически public static final)
interface Constants {
    double PI = 3.14159; // фактически public static final double PI = 3.14159;
    int MAX_SIZE = 100;
}
Важные нюансы: • Методы интерфейса неявно заданы как public (даже если не указано) • Все константы интерфейса неявно заданы как public static final • Начиная с Java 8, интерфейсы могут содержать реализацию default и static методов • Начиная с Java 9, интерфейсы могут включать private методы для указания внутри default-методов
Maxim Terletsky Уровень 24
2 августа 2026
Наследование интерфейсов (множественное). Интерфейс может наследоваться от нескольких интерфейсов.

interface Drawable {
    void draw();
}

interface Moveable {
    void move(int x, int y);
}

interface Clickable {
    void onClick();
}

// Множественное наследование интерфейсов
interface InteractiveElement extends Drawable, Moveable, Clickable {
    void highlight();
}
Почему это безопасно? • Интерфейсы содержат только константы (public static final), поэтому конфликта состояния не возникает. • Даже если два родительских интерфейса имеют одинаковые методы, конфликт решается просто - он один, и наследник должен его реализовать. Реализация интерфейса классом (implements). Класс реализует интерфейс с помощью ключевого слова implements. Если класс реализует несколько интерфейсов, они перечисляются через запятую.

interface Drawable {
    void draw();
}

interface Movable {
    void move(int x, int y);
}

// Класс реализует ДВА интерфейса
class Circle implements Drawable, Movable {
    private int x, y, radius;

    @Override
    public void draw() {
        System.out.println("Рисуем круг радиусом " + radius);
    }

    @Override
    public void move(int dx, int dy) {
        x += dx;
        y += dy;
    }
}
Важно: • Класс обязан реализовать все абстрактные методы интерфейса • Если класс не реализует хотя бы один метод, он обязан быть абстрактным
Maxim Terletsky Уровень 24
2 августа 2026
Когда использовать интерфейс, а когда абстрактный класс? Напишем небольшую рекомендацию, когда следует использовать интерфейсы и абстрактные классы: • Нужно описать способность (что умеет) → Интерфейс • Нужно описать сущность (кем является) → Абстрактный класс • Нужно общее состояние (поля) → Абстрактный класс • Нужна множественная классификация → Интерфейсы • Нужна гибкая подмена реализации → Интерфейс Пример написания абстрактного класса и интерфейсов:

// Человек - это сущность → абстрактный класс
abstract class Person {
    String name;
    int age;
    abstract void speak();
}

// Способности → интерфейсы
interface Swimmer { void swim(); }
interface Runner { void run(); }
interface Coder { void writeCode(); }

// Конкретный класс реализует способности
class Developer extends Person implements Coder, Swimmer {
    @Override
    void speak() { System.out.println("Hello!"); }
    @Override
    public void writeCode() { System.out.println("Writing Java..."); }
    @Override
    public void swim() { System.out.println("Swimming..."); }
}
Maxim Terletsky Уровень 24
2 августа 2026
Функциональные интерфейсы. Интерфейс, содержащий ровно один абстрактный метод, называется функциональным. При этом default, static и private методы не учитываются при подсчёте абстрактных методов.

@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);
}

// Использование через лямбду
Calculator add = (a, b) -> a + b;
Calculator multiply = (a, b) -> a * b;

System.out.println(add.calculate(5, 3));    // 8
System.out.println(multiply.calculate(5, 3)); // 15
Как видим функциональные интерфейсы можно использовать с лямбда-выражениями (Java 8+). Лямбда-выражение - это сокращённая запись создания реализации функционального интерфейса. Оно позволяет передавать поведение как объект без объявления отдельного класса. Встроенные функциональные интерфейсы Java. Java предоставляет готовые функциональные интерфейсы в пакете java.util.function. Про них можешь в документации почитать. Три часто используемых функциональных интерфейса: • Predicate<T> - проверка условия • Function<T, R> - преобразование типа • Consumer<T> - действие без возврата. Частый вариант использования данных функциональных интерфейсов - Stream API:

import java.util.*;
import java.util.function.*;

public class Main {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("cat", "elephant", "dog", "giraffe");        
        words.stream()
             .filter(s -> s.length() > 3)      // Predicate
             .map(String::toUpperCase)          // Function
             .forEach(System.out::println);     // Consumer
             // ELEPHANT
             // GIRAFFE
    }
}
Maxim Terletsky Уровень 24
2 августа 2026
Default методы и static методы (Java 8). До Java 8 интерфейсы содержали только абстрактные методы. С Java 8 появилась возможность добавлять методы с реализацией через default и static.

interface Drawable {
    void draw();

    // default метод - общая логика для всех реализаций
    default void drawTwice() {
        draw();
        draw();
    }

    // static метод - утилитарный
    static void printDrawingInfo() {
        System.out.println("Рисуем фигуру...");
    }
}

// Обычный класс-реализация
class Circle implements Drawable {
    @Override
    public void draw() {
        System.out.println("Рисуем круг");
    }
}

// Ещё одна реализация
class Square implements Drawable {
    @Override
    public void draw() {
        System.out.println("Рисуем квадрат");
    }
}

public class Main {
    public static void main(String[] args) {
        Drawable.printDrawingInfo(); // static метод

        Circle circle = new Circle();
        circle.drawTwice(); // default метод

        // Полиморфизм через интерфейс
        Drawable drawable = new Circle();
        drawable.draw(); // Рисуем круг

        drawable = new Square();
        drawable.draw(); // Рисуем квадрат
    }
}
Под комментарием "Полиморфизм через интерфейс" видим, что интерфейс позволяет работать с объектами разных классов через единый контракт, не привязываясь к конкретной реализации. Это главная причина существования интерфейсов. Нюансы: • Default методы можно переопределять в классах-реализациях • Если два интерфейса имеют одинаковые default методы - конфликт, нужно переопределить • Внутри default методов могут использоваться private методы (Java 9+) • static методы интерфейса не становятся методами класса-реализации и вызываются только через имя интерфейса: Drawable.printDrawingInfo()
Maxim Terletsky Уровень 24
2 августа 2026
Private методы в интерфейсах (Java 9+). Private методы решают проблему дублирования кода внутри default-методов интерфейса.

@FunctionalInterface
interface Drawable {
    void draw();

    default void drawWithBorder() {
        draw();
        drawBorder();
    }

    // private метод (Java 9+) - вспомогательный
    private void drawBorder() {
        System.out.println("Рисуем границу");
    }
}

public class Main {
    public static void main(String[] args) {
        Drawable circle = () -> System.out.println("Рисуем круг");
        Drawable square = () -> System.out.println("Рисуем квадрат");

        circle.drawWithBorder();
        // Рисуем круг
        // Рисуем границу

        square.drawWithBorder();
        // Рисуем квадрат
        // Рисуем границу
    }
}
Правила использования private методов: • Только внутри интерфейса - нельзя вызвать из класса-реализации • Могут быть статическими - для статических методов интерфейса • Не могут быть abstract - должны иметь реализацию • Помогают избежать дублирования кода в default-методах Пример разрешения конфликта default-методов:

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

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

// Класс реализует два интерфейса с одинаковым default-методом
// Обязательно переопределяем метод или пишем свою реализацию, чтобы разрешить конфликт
class Test implements A, B {
    @Override
    public void print() {
        A.super.print(); // явно выбираем реализацию из интерфейса A
        // или B.super.print(); // или можно выбрать B
        // или System.out.println("C"); // написать свою реализацию 
    }
}

public class Main {
    public static void main(String[] args) {
        Test test = new Test();
        test.print(); // A
    }
}
Maxim Terletsky Уровень 24
2 августа 2026
Маркерные интерфейсы. Это интерфейсы, которые не содержат ни одного метода. Их единственная цель - "пометить" класс, чтобы указать, что он обладает каким-то специальным свойством или поведением. Встроенные маркерные интерфейсы Java:

// 1. Serializable - помечает, что объект можно преобразовать в байты для сохранения/передачи
public interface Serializable {
    // Полностью пустой!
}

// 2. Cloneable - разрешает клонирование объекта через Object.clone()
public interface Cloneable {
    // Тоже пустой!
}

// 3. RandomAccess - сигнализирует, что доступ к элементам коллекции быстрый (O(1))
public interface RandomAccess {
    // Пустой интерфейс
}
Пример использования (Serializable):

import java.io.*;

// Маркерный интерфейс Serializable
class User implements Serializable {
    String name;
    int age;

    User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

public class Main {
    public static void main(String[] args) throws IOException {
        User user = new User("Alice", 25);

        // Проверка маркера
        if (user instanceof Serializable) {
            System.out.println("Объект можно сериализовать!");
        }
    }
}
Maxim Terletsky Уровень 24
2 августа 2026
Адаптеры. Класс-адаптер (Adapter Class) - это абстрактный класс с пустой реализацией всех методов интерфейса. Класс-адаптер обычно объявляют абстрактным, так как он предназначен для наследования, а не для создания экземпляров. Клиентский код - это любой код, который использует интерфейс, абстрактный класс или их конкретные реализации. Клиент может унаследоваться от адаптера и переопределить только нужные методы.

// 1. Интерфейс с множеством методов
interface MouseListener {
    void onClick();
    void onDoubleClick();
    void onRightClick();
    void onHover();
    void onDrag();
}

// 2. Адаптер - пустая реализация всех методов
abstract class MouseAdapter implements MouseListener {
    @Override public void onClick() {}
    @Override public void onDoubleClick() {}
    @Override public void onRightClick() {}
    @Override public void onHover() {}
    @Override public void onDrag() {}
}

// 3. Клиент - переопределяет только нужные методы
class MyMouseHandler extends MouseAdapter {
    @Override
    public void onClick() {
        System.out.println("Клик обработан!");
    }

    @Override
    public void onDoubleClick() {
        System.out.println("Двойной клик!");
    }
}

public class Main {
    public static void main(String[] args) {
        MyMouseHandler handler = new MyMouseHandler();
        handler.onClick();        // Клик обработан!
        handler.onDoubleClick();  // Двойной клик!
        handler.onRightClick();   // ничего не делает
        handler.onHover();        // ничего не делает
    }
}
Преимущества адаптеров: • Уменьшение boilerplate кода - не нужно писать пустые реализации. • Читаемость - видно только важные методы • Поддержка - после обновления адаптера (добавления новых пустых методов) существующие классы-наследники не потребуют изменений. • Гибкость - клиент выбирает только то, что ему нужно
Maxim Terletsky Уровень 24
2 августа 2026
Далее упомяну пару моментов и можно закругляться 😅 Хорошая практика объявления переменных. Рекомендуется объявлять переменные через интерфейс, а не через конкретную реализацию. Это уменьшает связанность кода и позволяет легко заменить реализацию (например, ArrayList на LinkedList).

List<String> list = new ArrayList<>(); // через интерфейс - ГУД
// ArrayList<String> list = new ArrayList<>(); // не есть ГУД
Одновременное использование extends и implements. Ничто не мешает одновременно использовать ключевые слова extends и implements.

class Dog extends Animal implements Runnable {
    // Сначала extends (класс), потом implements (интерфейсы)
}
На этом все, друг. Надеюсь тебе понравилось, и JavaRush в будущем доработает свою лекцию по интерфейсам (да и по абстрактным классам тоже). Удачной учебы 👍
Grigoryvvv Уровень 26 Expert
19 июля 2026
19.07.2026 / 18 уровень
Victoria Уровень 28
4 марта 2026
Интерфейс — это способ описать поведение объекта без привязки к его конкретной реализации. Используя интерфейс как тип переменной, я сознательно ограничиваю взаимодействие объектом только тем поведением, которое необходимо в данной задаче. Объект может уметь гораздо больше, но код зависит только от минимально необходимого контракта. Это позволяет: - зависеть от возможностей, а не от конкретного класса; - легко заменять одну реализацию другой; - уменьшать связанность кода; - делать систему более гибкой и расширяемой. Интерфейс — это не «урезанный класс», а способ описать роль, в которой объект используется. Примеры: - Смартфон может быть камерой, телефоном или навигатором. Если я держу его как Camera, для меня он всего лишь устройство, которое умеет фотографировать. - Мне всё равно, какая у тебя система платежей и как ты пополняешь счет — сейчас важно лишь то, что я могу списать деньги.
Rei Уровень 32
11 февраля 2026

На самом деле у интерфейсов все методы – public
Кроме тех, что помечены модификатором private 😅

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

class Student {
    private String name;
    ...
    public String getName() { ... }
}
Стало:

interface Student {
    String getName();
}

class StudentImpl implements Student {
    private String name;
    ...
}

Что это значит по сути? Раньше у нас был один конкретный класс. Теперь: Student — это контракт (что студент должен уметь) StudentImpl — это конкретная реализация Вот ключевая идея Теперь в main:

Student student = new StudentImpl("Ali");
Тип переменной — Student Объект — StudentImpl Это и есть: Программирование через интерфейс, а не через реализацию. Зачем это нужно? Чтобы можно было подставить другую реализацию. Например:

class LazyStudent implements Student { ... }
class OnlineStudent implements Student { ... }
class ExchangeStudent implements Student { ... }
И теперь код может работать с любым Student, не зная какой именно это класс. Вот что они называют: «класс для всех» «класс для своих» Это ужасная формулировка, да. На нормальном языке: Интерфейс — общий тип для системы Реализация — конкретная версия
C0N5P1RACY Уровень 1
4 января 2026
Без ГПТ тут не разобраться...
2 декабря 2025
Статья написана так, что скорее запутывает, чем объясняет.
Rei Уровень 32
11 февраля 2026
Полностью согласна
Exaltyr777 Уровень 25
4 августа 2025
Так и не понял из лекции зачем нужны интерфейсы, надеюсь дальше получше объяснят
Anonymous #3585174 Уровень 33
22 июля 2025
Like
@itArarat Уровень 32
11 декабря 2025
хватит уже под каждой лекцией писать Like
15 мая 2025
боже как это все запомнить......