Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Интерфейсы

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

Объявление и реализация

Интерфейс объявляют словом interface, а класс подключает его словом implements. Методы интерфейса по умолчанию открытые (public) и абстрактные, поэтому в реализации их тоже помечают public:

interface Greeter {
    String greet(String name);
}
 
class FriendlyGreeter implements Greeter {
    @Override
    public String greet(String name) {
        return "Привет, " + name + "!";
    }
}
 
void main() {
    Greeter greeter = new FriendlyGreeter();
    IO.println(greeter.greet("Анна"));
}

Результат:

Привет, Анна!

Интерфейс как тип

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

interface Priced {
    double price();
}
 
class Book implements Priced {
    @Override
    public double price() {
        return 15;
    }
}
 
class Delivery implements Priced {
    @Override
    public double price() {
        return 5;
    }
}
 
void main() {
    Priced[] cart = { new Book(), new Book(), new Delivery() };
    double total = 0;
    for (Priced item : cart) {
        total += item.price();
    }
    IO.println("Итого: " + total + " €");
}

Результат:

Итого: 35.0 €

Книга и доставка не имеют общего родителя, но обе знают свою цену — этого достаточно.

Несколько интерфейсов

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

interface Swimmer {
    String swim();
}
 
interface Flyer {
    String fly();
}
 
class Duck implements Swimmer, Flyer {
    @Override
    public String swim() {
        return "Утка плывёт";
    }
 
    @Override
    public String fly() {
        return "Утка летит";
    }
}
 
void main() {
    Duck duck = new Duck();
    IO.println(duck.swim());
    IO.println(duck.fly());
}

Результат:

Утка плывёт
Утка летит

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

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

interface Greeter {
    String name();
 
    default String greet() {
        return "Привет, " + name() + "!";
    }
 
    static Greeter of(String name) {
        return () -> name;
    }
}
 
class Robot implements Greeter {
    @Override
    public String name() {
        return "R2";
    }
}
 
void main() {
    IO.println(new Robot().greet());
    IO.println(Greeter.of("Олег").greet());
}

Результат:

Привет, R2!
Привет, Олег!

Запись () -> name — это лямбда, короткий способ реализовать интерфейс с одним абстрактным методом. Лямбды подробно разобраны в главе об обобщениях и лямбдах.

Интерфейс или абстрактный класс

Интерфейс описывает умение («умеет плавать», «имеет цену») и не хранит состояние объекта: полей у него нет, только константы. Абстрактный класс подходит, когда у подклассов есть общие поля и код. Если сомневаетесь, начинайте с интерфейса — класс может реализовать много интерфейсов, но унаследовать только один класс.

Частая ошибка

Методы интерфейса — public. Если в классе забыть это слово, метод станет менее доступным, чем в интерфейсе, и компилятор откажется:

interface Greeter {
    String greet(String name);
}
 
class FriendlyGreeter implements Greeter {
    String greet(String name) {
        return "Привет, " + name;
    }
}
 
void main() {
}

Результат:

Main.java:6: error: greet(String) in Main.FriendlyGreeter cannot implement greet(String) in Greeter
    String greet(String name) {
           ^
  attempting to assign weaker access privileges; was public

Добавьте public перед методом в классе.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.