Ведение журнала по методам интерфейса по умолчанию

Приветствуйте всех гуру Java!

Так как Java8 мы можем иметь реализации по умолчанию в интерфейсах (yay!). Однако проблема возникает, когда вы хотите использовать метод по умолчанию.

У меня такое чувство, что неразумно вызывать .getLogger() каждый раз, когда я хочу что-то записывать по умолчанию.

Да, можно определить статическую переменную в интерфейсе, но это не является хорошей практикой для интерфейсов в любом случае +, он предоставляет журнал (должен быть общедоступным).

Решение, которое у меня есть на данный момент:

interface WithTimeout<Action> {

    default void onTimeout(Action timedOutAction) {
        LogHolder.LOGGER.info("Action {} time out ignored.", timedOutAction);
    }

    static final class LogHolder {
        private static final Logger LOGGER = getLogger(WithTimeout.class);
    }
}

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

Знает ли кто-нибудь о лучшем решении?:)

EDIT: я использую SLF4J, поддерживаемый Logback

Ответ 1

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

public interface WithTimeout<Action> {

    default void onTimeout(Action timedOutAction) {
        LogHolder.LOGGER.info("Action {} time out ignored.", timedOutAction);
    }
}
final class LogHolder { // not public
    static final Logger LOGGER = getLogger(WithTimeout.class);
}

Ответ 2

Здесь вы идете.

Логгер закрыт для интерфейса. Никто кроме этого интерфейса и его методы по умолчанию не могут получить доступ к чему-либо внутри Test2. И ничто не может расширить класс Test2.

Никто не предлагает вам сделать это... но он работает! И это отличный способ получить доступ к регистратору классов для основного интерфейса и, возможно, умный способ сделать что-то еще не совсем безумное.

Это действительно то же самое, что и LogHolder в вопросе OP, за исключением того, что класс Test2 - это все частные методы и конструктор private, а класс не помечен как static.

И в качестве дополнительного бонуса он сохраняет состояние, статически и для каждого экземпляра. (Не делайте этого в реальной программе, пожалуйста!)

public class TestRunner {
    public static void main(String[] args) {
        Test test = new Test() {
        };
        test.sayHello("Jane");
        System.out.println("Again");
        test.sayHello("Bob");
    }
}
public interface Test {
    default void sayHello(String name) {
        Logger log = Test2.log;
        Test2 ref = Test2.getMine.apply(this);
        int times = ref.getTimes();
        for (int i = 0; i < times; i++) {
            System.out.println(i + ": Hello " + name);
        }
        log.info("just said hello {} times :)",times);
    }
    final class Test2 {
        private static final Logger log = LoggerFactory.getLogger(Test.class);
        private static final Map lookup = new WeakHashMap();
        private static final Function getMine = (obj) -> {
            return lookup.computeIfAbsent(obj, (it) -> new Test2());
        };
        private int calls = 0;
        private Test2() {
        }
        private void setCalls(int calls) {
            this.calls = calls;
        }
        private int getCalls() {return calls;}
        private int getTimes() {return ++calls;}
    }
}

Ответ 3

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