Статические и стандартные методы в интерфейсах в Java

Статические и стандартные методы в интерфейсах в Java

1. обзор

Java 8 представила в таблице несколько совершенно новых функций, включаяlambda expressions,functional interfaces,method references,streams,Optional иstatic и default в интерфейсах.

Некоторые из них уже покрытыthis article. Тем не менее, методыstatic иdefault в интерфейсах заслуживают более глубокого изучения.

В этой статье мы подробно обсудимhow to use static and default methods in interfaces и рассмотрим некоторые варианты использования, в которых они могут быть полезны.

2. Зачем нужны методы по умолчанию в интерфейсах

Как и в обычных интерфейсных методах,default methods are implicitly public - модификаторpublic указывать не нужно.

В отличие от обычных методов интерфейса, этоdeclared with the default keyword at the beginning of the method signature, а ониprovide an implementation.

Давайте посмотрим на простой пример:

public interface MyInterface {

    // regular interface methods

    default void defaultMethod() {
        // default method implementation
    }
}

Причина, по которой методыdefault были включены в выпуск Java 8, довольно очевидна.

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

Методы интерфейса по умолчанию являются эффективным способом решения этой проблемы. Ониallow us to add new methods to an interface that are automatically available in the implementations. Таким образом, нет необходимости изменять реализующие классы.

Таким образом,backward compatibility is neatly preserved без необходимости рефакторинга разработчиков.

3. Методы интерфейса по умолчанию в действии

Чтобы лучше понять функциональность методов интерфейсаdefault, давайте создадим простой пример.

Допустим, у нас есть наивный интерфейсVehicle и всего одна реализация. Могло быть и больше, но давайте будем простыми:

public interface Vehicle {

    String getBrand();

    String speedUp();

    String slowDown();

    default String turnAlarmOn() {
        return "Turning the vehicle alarm on.";
    }

    default String turnAlarmOff() {
        return "Turning the vehicle alarm off.";
    }
}

И давайте напишем реализующий класс:

public class Car implements Vehicle {

    private String brand;

    // constructors/getters

    @Override
    public String getBrand() {
        return brand;
    }

    @Override
    public String speedUp() {
        return "The car is speeding up.";
    }

    @Override
    public String slowDown() {
        return "The car is slowing down.";
    }
}

Наконец, давайте определим типичный классmain, который создает экземплярCar и вызывает его методы:

public static void main(String[] args) {
    Vehicle car = new Car("BMW");
    System.out.println(car.getBrand());
    System.out.println(car.speedUp());
    System.out.println(car.slowDown());
    System.out.println(car.turnAlarmOn());
    System.out.println(car.turnAlarmOff());
}

Обратите внимание, что методыdefaultturnAlarmOn() иturnAlarmOff() из нашего интерфейсаVehicle являютсяautomatically available in the Car class.

Более того, если в какой-то момент мы решим добавить больше методовdefault к интерфейсуVehicle, приложение все равно продолжит работу, и нам не придется заставлять класс предоставлять реализации для новых методов. .

Наиболее типичное использование методов по умолчанию в интерфейсах -to incrementally provide additional functionality to a given type without breaking down the implementing classes.

Кроме того, их можно использовать дляprovide additional functionality around an existing abstract method:

public interface Vehicle {

    // additional interface methods

    double getSpeed();

    default double getSpeedInKMH(double speed) {
       // conversion
    }
}

4. Правила наследования нескольких интерфейсов

Методы интерфейса по умолчанию - довольно приятная особенность, но с некоторыми оговорками, которые стоит упомянуть. Поскольку Java позволяет классам реализовывать несколько интерфейсов, важно знатьwhat happens when a class implements several interfaces that define the same default methods.

Чтобы лучше понять этот сценарий, давайте определим новый интерфейсAlarm и проведем рефакторинг классаCar:

public interface Alarm {

    default String turnAlarmOn() {
        return "Turning the alarm on.";
    }

    default String turnAlarmOff() {
        return "Turning the alarm off.";
    }
}

С этим новым интерфейсом, определяющим собственный набор методовdefault, классCar будет реализовывать какVehicle, так иAlarm:

public class Car implements Vehicle, Alarm {
    // ...
}

В этом случаеthe code simply won’t compile, as there’s a conflict caused by multiple interface inheritance (он жеDiamond Problem). КлассCar унаследует оба набора методовdefault. Какие из них должны быть названы тогда?

Чтобы решить эту двусмысленность, мы должны явно предоставить реализацию для методов:

@Override
public String turnAlarmOn() {
    // custom implementation
}

@Override
public String turnAlarmOff() {
    // custom implementation
}

Мы также можемhave our class use the default methods of one of the interfaces.

Давайте посмотрим на пример, в котором используются методыdefault из интерфейсаVehicle:

@Override
public String turnAlarmOn() {
    return Vehicle.super.turnAlarmOn();
}

@Override
public String turnAlarmOff() {
    return Vehicle.super.turnAlarmOff();
}

Точно так же мы можем заставить класс использовать методыdefault, определенные в интерфейсеAlarm:

@Override
public String turnAlarmOn() {
    return Alarm.super.turnAlarmOn();
}

@Override
public String turnAlarmOff() {
    return Alarm.super.turnAlarmOff();
}

Кроме того, это дажеpossible to make the Car class use both sets of default methods:

@Override
public String turnAlarmOn() {
    return Vehicle.super.turnAlarmOn() + " " + Alarm.super.turnAlarmOn();
}

@Override
public String turnAlarmOff() {
    return Vehicle.super.turnAlarmOff() + " " + Alarm.super.turnAlarmOff();
}

5. Статические методы интерфейса

Помимо возможности объявлять методыdefault в интерфейсах,Java 8 allows us to define and implement static methods in interfaces.

Поскольку методыstatic не принадлежат конкретному объекту, они не являются частью API классов, реализующих интерфейс, и должны бытьcalled by using the interface name preceding the method name.

Чтобы понять, как методыstatic работают в интерфейсах, давайте проведем рефакторинг интерфейсаVehicle и добавим к нему служебный методstatic:

public interface Vehicle {

    // regular / default interface methods

    static int getHorsePower(int rpm, int torque) {
        return (rpm * torque) / 5252;
    }
}

Defining a static method within an interface is identical to defining one in a class. Более того, методstatic может быть вызван внутри других методовstatic иdefault.

Теперь предположим, что мы хотим вычислитьhorsepower двигателя данного автомобиля. Мы просто вызываем методgetHorsePower():

Vehicle.getHorsePower(2500, 480));

Идея, лежащая в основе методов интерфейсаstatic, состоит в том, чтобы предоставить простой механизм, который позволяет намincrease the degree of cohesion проекта, объединяя связанные методы в одном месте без необходимости создания объекта.

В значительной степениthe same can be done with abstract classes.. Основное различие заключается в том, чтоabstract classes can have constructors, state, and behavior.

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

6. Заключение

В этой статье мы подробно изучили использование методов интерфейсаstatic иdefault в Java 8. На первый взгляд, эта функция может показаться немного небрежной, особенно с точки зрения объектно-ориентированного пуризма. В идеале интерфейсы не должны инкапсулировать поведение и должны использоваться только для определения общедоступного API определенного типа.

Однако когда дело доходит до обеспечения обратной совместимости с существующим кодом, методыstatic иdefault являются хорошим компромиссом.

И, как обычно, доступны все примеры кода, показанные в этой статьеover on GitHub.