Как известно, начиная с версии JDK 8.0 в Java наряду с так называемыми дефолтными методами появились также и статические методы. Например, до появления Java 8 нельзя было прописывать подобный интерфейс:

public interface IStaticMetodContainer {

    static void printed(String s) {

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

    }

}

        Тем не менее, была возможность использовать статический вложенный класс в интерфейсе (начиная с версии Java 2.0). Используя возможности только Java 2.0 этот же пример выше мог бы выглядеть, например, следующим образом:

public interface IClassContainer {

    class StaticNestedClass{

        public static void printed(String s) {

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

        }

    }

}

Создадим специальный класс IClassTest для проверки работы методов. И запустим метод main:

public class IClassTest {

    public static void main(String[] args) {

        IStaticMetodContainer.printed("test");

        IClassContainer.StaticNestedClass.printed("test");

    }

}

Как видим, результат идентичен для обоих вариантов:

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

printing::test

printing::test

Process finished with exit code 0

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

Еще одной интересной особенностью статических методов в интерфейсах является невозможность переопределения подобного метода в классе, имплементирующем интерфейс. Статические методы являются частью интерфейса и не «передаются» классу, поэтому можно сказать, что переопределение в данном случае не то, что невозможно, но и непредусмотрено.  Поэтому, можно в классе-имплементоре спокойно реализовывать свой метод с идентичной сигнатурой, однако, он все равно не заменит аналогичный метод в интерфейсе и не будет сообщений от IDE с требованием вставить аннотацию @Override. Более того, использование аннотации @Override даст ошибку компиляции. Рассмотрим на следующем примере, предварительно немного видоизменив исходный интерфейс IStaticMetodContainer и добавив в него дефолтный метод:


public interface IStaticMetodContainer {

    static void printed(String s) {

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

    }


    default void print(String s) {

       printed("default part included "+s);

    }

}


И определим следующий класс-имплементор для данного интерфейса:


public class IStaticMetodContainerImpl implements IStaticMetodContainer {


    static void printed(String s) {

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

        }