Результат выполнения кода в методе main:

"C:\Program Files\Java\jdk-13.0.2\bin\java.exe" -Didea.launcher.port=54625 "

printing::test

printing from class ::test

printing::default part included

 

Process finished with exit code 0

 

Как видно, даже в этом случае «внутренности» интерфейса не подменяются, дефолтный метод выполняется с неизменным статическим методом printed. А использование подобной конструкции даже не дало бы возможности скомпилировать код:

 

@Override

        static void printed(String s) {

            System.out.println("printing from class ::"+s);

        }

 

Таким образом, очевидно, что «неизменность» статических методов в интерфейсе удобна для поддержания целостности кода дефолтных методом. Потому что наряду с приватными дефолтными методами и статические методы также могут использоваться в дефолтных методах для «расшивки» и структурной организации кода.

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

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

Кроме рассмотренных выше способов использования статических классов в интерфейсах

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

 

@FunctionalInterface

interface Printer {

    void print(String text);

 

    static Printer A() {

        return text -> System.out.println("Printer A: " + text);

    }

    static Printer B() {

        return text -> System.out.println("Printer A: " + text);

    }

 

    static Printer C() {

        return text -> System.out.println("Printer C: " + text);

    }

}

 

Функциональный интерфейс позволяет нам эффективно использовать лямбда-выражения, а статические методы обладают тем же функционалом, что и внутренние статические классы, вложенные в этот интерфейс, при этом имплементирующие этот же интерфейс и реализующие, например, встроенные возможности заглушек или прочий функционал, представляя возможность создавать объекты типа данного интерфейса.

 

Запустим код:

 

public class PrinterTestDrive {

    public static void main(String[] args) {

        Printer.A().print("text");

        Printer.B().print("text");

        Printer.C().print("text");

    }

}

 

Результат:

"C:\Program Files\Java\jdk-13.0.2\bin\java.exe" -Didea.launcher.port=54890 "-

Printer A: text

Printer A: text

Printer C: text

 

Process finished with exit code 0