Статьи

Java 8: объявление частных и защищенных методов в интерфейсах

Когда появилась Java 8, мы могли использовать методы по умолчанию в интерфейсах. Основным драйвером этой функции было расширение интерфейса при сохранении обратной совместимости для более старых версий интерфейса. Одним из примеров является введение метода stream() в существующие классы Collection .

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

Но есть проблема. Частный и защищенный доступ в интерфейсах был отложен до Java 9. Итак, как мы можем использовать методы частного интерфейса в Java 8 сегодня?

Простое решение

Предположим, что у нас есть интерфейс Foo с двумя методами; bar() и bazz() которые оба должны возвращать какой-то сложный для вычисления результат, исходящий из некоторого общего кода, подобного этому:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public interface Foo {
 
    default int bar() {
        return complicatedMethodWithManyLinesOfCode();
    }
 
    default int bazz() {
        return complicatedMethodWithManyLinesOfCode() + 1;
    }
 
     
    // Will not work in Java 8 because interface methods cannot be private!
    private int complicatedMethodWithManyLinesOfCode() {
        // Actual code not shown...
        return 0;
    }
 
}

Вводя class который содержит приватный метод, мы можем «спрятать» метод от внешнего доступа и почти уйти с приватными методами в интерфейсе Java 8. Это можно сделать так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public interface Foo {
 
    default int bar() {
        return Hidden.complicatedMethodWithManyLinesOfCode();
    }
 
    default int bazz() {
        return Hidden.complicatedMethodWithManyLinesOfCode() + 1;
    }
 
    class Hidden {
 
        private static int complicatedMethodWithManyLinesOfCode() {
            // Actual code not shown...
            return 0;
        }
    }
 
}

Метод Foo:complicatedMethodWithManyLinesOfCode не виден извне классов или интерфейсов, но виден сам класс Hidden . Однако методы и поля в Hidden нельзя увидеть, если они закрытые.

Эта схема также может применяться для доступа к защищенному методу интерфейса. Технически, мы могли бы расширить класс Hidden в интерфейсе, который также расширяет оригинальный интерфейс Foo . Помните, что защищенные методы также видны в пакете, поэтому, если мы расширяем или используем интерфейс из одного и того же пакета, защищенные методы становятся видимыми (как и всегда).

Один недостаток заключается в том, что скрытые методы не могут получить доступ к другим методам в интерфейсе. Этот последний недостаток можно легко устранить, если скрытый статический метод получит параметр типа интерфейса. Предположим, что для метода complexMethodWithManyLinesOfCode требуется другое значение из интерфейса Foo которое можно получить с помощью некоторого метода интерфейса с именем buzz() , тогда он может выглядеть примерно так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public interface Foo {
 
    default int bar() {
        return Hidden.complicatedMethodWithManyLinesOfCode(this);
    }
 
    default int bazz() {
        return Hidden.complicatedMethodWithManyLinesOfCode(this) + 1;
    }
 
    int buzz();
 
    class Hidden {
 
        private static int complicatedMethodWithManyLinesOfCode(Foo foo) {
            // Actual code not shown...
            return 0 + foo.buzz();
        }
    }
 
}